C Argument number to create PGM images - arrays

I have to do a task for university in C. The program creates images in PGM format.The program compiles successfully, however, I can't run the program correctly. At the beginning of the main function the if operator needs 2 argc, but every time I try to run the program I just have one argc and I get the result
"Error: missing or bad parameters, usage a3 [imagefile]"
The code is:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
const double PI = 3.1415926535897932;
int** loadImage (const char *filename, int* width, int* height);
bool saveImage (const char *filename, int **pixels,
const unsigned int width, const unsigned int height);
/* *** task (a) *** */
int normalize(int value) {
return 0; // Dummy return
}
/* *** task (b) *** */
int** createImage(unsigned int width, unsigned int height, int value){
return NULL; // Dummy return
}
/* *** task (c) *** */
void freeImage(int** pixels, unsigned int width, unsigned int height){
}
/* *** task (d) *** */
int** inverter (int **pixels, int width, int height)
{
return NULL; // Dummy return
}
/* *** task (e) *** */
int** laplace (int **pixels, int width, int height)
{
return NULL; // Dummy return
}
/* *** task (f) *** */
int** rotate (int** pixels, int width, int height, double angle, int background)
{
return NULL; // Dummy return
}
int main (int argc, char **argv)
{
if (argc == 2)
{
int width=0, height=0;
int **image = loadImage (argv[1], &width, &height);
if (image != NULL)
{
int **image1 = inverter (image, width, height);
int **image2 = laplace (image, width, height);
int **image3 = rotate (image, width, height, 150, 128);
saveImage ("image0.pgm", image, width, height);
saveImage ("image1.pgm", image1, width, height);
saveImage ("image2.pgm", image2, width, height);
saveImage ("image3.pgm", image3, width, height);
freeImage (image, width, height);
freeImage (image1, width, height);
freeImage (image2, width, height);
freeImage (image3, width, height);
}
}
else {
printf ("Error: missing or bad parameters, usage a3 [imagefile]\n");
}
return 0;
}
/* *** Bild aus Datei laden *** */
int** loadImage (const char *filename, int* width, int* height)
{
const char *delimiter = " \n\r\t";
FILE *file = fopen (filename, "rt");
int** pixels;
bool isValid = true;
if (file != NULL)
{
char line [80];
int n = 0, params [3];
if (fgets (line, 80, file) == NULL) isValid = false;
if (strlen (line) != 3 || strncmp (line, "P2", 2)) isValid = false;
while (isValid && fgets (line, 80, file) != NULL)
{
if (strlen (line) <= 71)
{
if (line[0] == '#') continue;
char *p = strtok (line, delimiter);
while (isValid && p != NULL)
{
if (n < 3)
{
int a = atoi (p);
if (a <= 0) {isValid = false;}
else {params[n++] = a;}
}
else
{
if (n == 3)
{
if (params[2] != 255) {isValid = false;}
else {
pixels = createImage (params[0], params[1], 0);
*width = params[0];
*height = params[1];
}
}
if (n-3 < *width * *height)
{
int x = (n-3) % *width;
int y = (n-3) / *width;
int value = atoi (p);
pixels[y][x] = normalize(value);
n++;
}
else {isValid = false;}
}
p = strtok (NULL, delimiter);
}
}
else { isValid = false;}
}
fclose (file);
if (n-3 != *width * *height) {isValid = false;}
if (!isValid) freeImage (pixels, *width, *height);
}
return isValid ? pixels : NULL;
}
/* *** Bild in Datei speichern *** */
bool saveImage (const char *filename, int **pixels, unsigned int width, unsigned int height)
{
FILE *file = fopen (filename, "wt");
if (file != NULL && pixels != NULL)
{
fprintf (file, "P2\n");
fprintf (file, "%d %d\n", width, height);
fprintf (file, "%d\n", 255);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
fprintf (file, "%d\n", pixels[y][x]);
}
}
fclose (file);
return true;
}
return false;
}
The professor also said we should add following argument to the task in VSCode:
"args": [
"-g",
"-std=c99",
"-Wall",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}",
"-lm"
],

Your program a3.exe requires an argument which is the file name. To run it from command line, enter
.\a3.exe YourFilename
replacing YourFilename with the actual file name you want to use.

Related

Fragment BMP image by 8x8 blocks

I'm writing a BMP image parser, I wrote the read and write functions, which works fine while reading B&W, grayscale, and color images. I'm writing a function for fragment a given image in 8x8 pixel images, so, if I have a 72x48 image, I will have 56 images. I already crop the pixels block, but when I write the images, these images have colors that the original didn't have, or are completely black.
The original image, the first 8x8 fragment, the second (the rest of the fragments are equals to this) 8x8 fragment.
I'm pretty confused because I already can read and copy the original image and write a copy.
Also, I crop an 8x8 fragment with a web tool, and some headers differ from the header that I calc. For example, the size of the whole file, which appears next to the image type is 172, while I calc 92. The header size is also different.
In order to fragment the image, I calc the number of fragments by multiplying the number of row by the number of columns, then I allocate memory for a list of images, where the fragments are saved, then I start a 3 nested for loop, where the first one is for allocated every image in the list and is jointed to the fragment number, the second and the third one is dedicated to copy pixel by pixel from the original to the block.
Could someone give a hint of what I could be doing wrong?
bmp.h
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdbool.h>
#include <math.h>
#include <string.h>
typedef uint16_t ImageType;
typedef struct
{
uint32_t size;
uint16_t additionalFeature;
uint16_t copy;
uint32_t offset;
} ImageHeader;
typedef struct
{
uint32_t headerSize;
int width;
int height;
uint16_t colorSpaces;
uint16_t bitsPerPixel;
uint32_t compression;
uint32_t size;
int verticalResolution;
int horizontalResolution;
uint32_t totalColors;
uint32_t importantColors;
} ImageMetadata;
typedef struct
{
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t reserved;
} ImageColor;
typedef ImageColor *ImageColors;
typedef char *ImagePixels;
typedef struct
{
ImageType type;
ImageHeader header;
ImageMetadata metadata;
ImageColors colors;
ImagePixels pixels;
} Image;
ImageType BMP_IMAGE_TYPE = 0x4D42;
unsigned int getImagePixelsSize(int width, int height, int bitsPerPixel);
unsigned int getImageSize(Image image);
void readImage(char *filename, Image *image);
void writeImage(char *filename, Image image);
void fragmentImage(Image image, Image *images, int fragmentWidth, int fragmentHeight);
void showImage(Image image);
bmp.c
#include "bmp.h"
unsigned int getImagePixelsSize(int width, int height, int bitsPerPixel)
{
return height * (int)(((bitsPerPixel * width) + 31) / 32) * 4;
}
unsigned int getImageSize(Image image)
{
return (
getImagePixelsSize(image.metadata.width, image.metadata.height, image.metadata.bitsPerPixel) +
sizeof(image.type) +
sizeof(image.header) +
sizeof(image.metadata) +
sizeof(image.colors)
);
}
void showImage(Image image)
{
printf("Size: %d \n", image.header.size);
printf("Offset: %d", image.header.offset);
printf("Width: %d \n", image.metadata.width);
printf("Height: %d \n", image.metadata.height);
printf("BPP: %d \n", image.metadata.bitsPerPixel);
printf("Header size: %d \n", image.metadata.headerSize);
printf("Total colors: %d \n", image.metadata.totalColors);
printf("Raw colors: %lu \n", sizeof(image.colors));
printf("Pixel size %d \n", getImagePixelsSize(image.metadata.width, image.metadata.height, image.metadata.bitsPerPixel));
printf("Calculated Image Size: %d \n", getImageSize(image));
}
void readImage(char *filename, Image *image)
{
FILE *imageFile = fopen(filename, "r");
if (!imageFile)
{
perror("ReadImageFileException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fread(&(image->type), sizeof(ImageType), 1, imageFile);
if (image->type != BMP_IMAGE_TYPE)
{
fprintf(stderr, "%hu", image->type);
perror("ReadImageTypeException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fread(&(image->header), sizeof(ImageHeader), 1, imageFile);
fread(&(image->metadata), sizeof(ImageMetadata), 1, imageFile);
if (image->metadata.bitsPerPixel <= 8)
{
image->colors = (ImageColor *)malloc(sizeof(ImageColor) * image->metadata.totalColors);
printf("Calculated raw colors %lu", sizeof(ImageColor) * image->metadata.totalColors);
fread(image->colors, sizeof(ImageColor) * image->metadata.totalColors, 1, imageFile);
}
int pixelsSize = getImagePixelsSize(image->metadata.width, image->metadata.height, image->metadata.bitsPerPixel);
image->pixels = (char *)malloc(sizeof(char) * pixelsSize);
if (!image->pixels)
{
perror("ReadImagePixelsException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fseek(imageFile, image->header.offset, SEEK_SET);
fread(image->pixels, sizeof(char) * pixelsSize, 1, imageFile);
fclose(imageFile);
}
void writeImage(char *filename, Image image)
{
FILE *imageFile = fopen(filename, "w+");
if (!imageFile)
{
perror("WriteImageFileException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
int typeWritten = fwrite(&(image.type), sizeof(ImageType), 1, imageFile);
if (typeWritten == 0)
{
perror("WriteImageTypeException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
int headerWritten = fwrite(&(image.header), sizeof(ImageHeader), 1, imageFile);
if (headerWritten == 0)
{
perror("WriteImageHeaderException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
int metadataWritten = fwrite(&(image.metadata), sizeof(ImageMetadata), 1, imageFile);
if (metadataWritten == 0)
{
perror("WriteImageMetadataException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
if (image.metadata.bitsPerPixel <= 8)
{
int colorsWritten = fwrite(image.colors, sizeof(ImageColor) * image.metadata.totalColors, 1, imageFile);
if (colorsWritten == 0)
{
perror("WriteImageColorException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
}
int pixelsSize = getImagePixelsSize(image.metadata.width, image.metadata.height, image.metadata.bitsPerPixel);
int pixelsWritten = fwrite(image.pixels, sizeof(char) * pixelsSize, 1, imageFile);
if (pixelsWritten == 0)
{
perror("WriteImagePixelsException");
fclose(imageFile);
exit(EXIT_FAILURE);
}
fclose(imageFile);
}
void fragmentImage(Image image, Image *images, int fragmentWidth, int fragmentHeight)
{
if (fragmentWidth > image.metadata.width)
{
fprintf(stderr, "FragmentSizeException: Fragment width is larger than image width");
}
if (fragmentHeight > image.metadata.height)
{
fprintf(stderr, "FragmentSizeException: Fragment height is larger than image width");
}
int rows = (int)(image.metadata.width / fragmentWidth);
int columns = (int)(image.metadata.height / fragmentHeight);
int fragments = rows * columns;
// 72 x 48 -> 8x8
// 54
// images[54]
images = (Image *)malloc(sizeof(Image) * fragments);
if (!images)
{
perror("FragmentImageMemoryException");
exit(EXIT_FAILURE);
}
int w = 0, h = 0;
for (int fragment = 0; fragment < fragments; fragment++)
{
(images + fragment)->type = image.type;
(images + fragment)->colors = image.colors;
(images + fragment)->header = image.header;
(images + fragment)->metadata = image.metadata;
(images + fragment)->metadata.width = fragmentWidth;
(images + fragment)->metadata.height = fragmentHeight;
int pixelsSize = getImagePixelsSize(fragmentWidth, fragmentHeight, image.metadata.bitsPerPixel);
(images + fragment)->pixels = (char *)malloc(sizeof(char) * pixelsSize);
if (!(images + fragment)->pixels)
{
perror("FragmentImageMemoryException");
exit(EXIT_FAILURE);
}
for (int j = 0; j < 8; j++, w++)
{
for (int k = 0; k < 8; k++, h++)
{
(images + fragment)->pixels[(w * 8) + k] = image.pixels[(w * image.metadata.width) + k];
}
}
(images + fragment)->header.size = getImageSize(*(images + fragment));
char *filename = malloc(sizeof(char) * 38);
snprintf(filename, 38, "./images/fragments/fragment-%d.bmp", fragment);
writeImage(filename, *(images + fragment));
}
}
main.c
#include "bmp.c"
#include <math.h>
int main(int argc, char *argv[])
{
Image image;
Image *images;
char *imageSourceName = argv[1];
char *imageTargeName = argv[2];
readImage(imageSourceName, &image);
writeImage(imageTargeName, image);
showImage(image);
fragmentImage(image, images, 8, 8);
return 0;
}

PNG file are truncated and cannot be shown by every application

I have a code that produce PNG file (using libpng).
I can open these files with EOG (Eye Of Gnome) but with GIMP, imagemagic and others I have an error.
Exiftool tells me that png file is truncated, but I don't see where. On EOG everything is ok.
The code:
int savepng(const char *name, fits *fit, uint32_t bytes_per_sample,
gboolean is_colour) {
int32_t ret = -1;
png_structp png_ptr;
png_infop info_ptr;
const uint32_t width = fit->rx;
const uint32_t height = fit->ry;
char *filename = strdup(name);
if (!ends_with(filename, ".png")) {
filename = str_append(&filename, ".png");
}
FILE *p_png_file = g_fopen(name, "wb");
if (p_png_file == NULL) {
return ret;
}
/* Create and initialize the png_struct with the desired error handler
* functions. If you want to use the default stderr and longjump method,
* you can supply NULL for the last three parameters. We also check that
* the library version is compatible with the one used at compile time,
* in case we are using dynamically linked libraries. REQUIRED.
*/
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(p_png_file);
return ret;
}
/* Allocate/initialize the image information data. REQUIRED */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fclose(p_png_file);
png_destroy_write_struct(&png_ptr, NULL);
return ret;
}
/* Set error handling. REQUIRED if you aren't supplying your own
* error handling functions in the png_create_write_struct() call.
*/
if (setjmp(png_jmpbuf(png_ptr))) {
/* If we get here, we had a problem writing the file */
fclose(p_png_file);
png_destroy_write_struct(&png_ptr, &info_ptr);
return ret;
}
/* Set up the output control if you are using standard C streams */
png_init_io(png_ptr, p_png_file);
/* Set the image information here. Width and height are up to 2^31,
* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
* or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
*/
if (is_colour) {
png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_DEFAULT);
uint32_t profile_len;
const unsigned char *profile = get_sRGB_profile_data(&profile_len);
if (profile_len > 0) {
png_set_iCCP(png_ptr, info_ptr, *name ? name : "icc", 0, (png_const_bytep) profile, profile_len);
}
} else {
png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
PNG_COLOR_TYPE_GRAY,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_DEFAULT);
}
/* Write the file header information. REQUIRED */
png_write_info(png_ptr, info_ptr);
png_bytep *row_pointers = malloc((size_t) height * sizeof(png_bytep));
int samples_per_pixel;
if (is_colour) {
samples_per_pixel = 3;
} else {
samples_per_pixel = 1;
}
if (bytes_per_sample == 2) {
/* swap bytes of 16 bit files to most significant bit first */
png_set_swap(png_ptr);
WORD *data = convert_data(fit);
for (unsigned i = 0, j = height - 1; i < height; i++)
row_pointers[j--] = (png_bytep) ((uint16_t*) data + (size_t) samples_per_pixel * i * width);
} else {
uint8_t *data = convert_data8(fit);
for (unsigned i = 0, j = height - 1; i < height; i++)
row_pointers[j--] = (uint8_t*) data + (size_t) samples_per_pixel * i * width;
}
png_write_image(png_ptr, row_pointers);
/* Clean up after the write, and free any memory allocated */
png_destroy_write_struct(&png_ptr, &info_ptr);
/* Close the file */
fclose(p_png_file);
free(row_pointers);
free(filename);
return 0;
}
Could someone point me out my error? Software that can't read the image just display an error dialog. That's all. So it is difficult for me to know where is the error.
As proposed by #MarkSetchell, one line was missing:
/* Clean up after the write, and free any memory allocated */
png_write_end(png_ptr, info_ptr); // this line was missing
png_destroy_write_struct(&png_ptr, &info_ptr);
That's all.
Thank you.
I think that png_write_image() only writes the image row data, so various headers or elements of meta-data are missing. I normally use png_write_png() to write the whole file.
I've attached some code that definitely works for me, in that the output can be read by Gimp, etc. I don't claim it's production-quality ;)
int bitmap_write_png (const bitmap_t *bitmap, const char *path)
{
int ret = -1;
size_t x, y;
int pixel_size = 3;
int depth = 8;
FILE *fp = fopen (path, "wb");
if (fp)
{
ret = 0;
png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
png_infop info_ptr = info_ptr = png_create_info_struct (png_ptr);
png_set_IHDR (png_ptr,
info_ptr,
bitmap->width,
bitmap->height,
depth,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_byte ** row_pointers = png_malloc (png_ptr,
bitmap->height * sizeof (png_byte *));
for (y = 0; y < bitmap->height; y++)
{
png_byte *row =
png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * pixel_size);
row_pointers[y] = row;
for (x = 0; x < bitmap->width; x++)
{
pixel_t *pixel = pixel_at_const (bitmap, x, y);
*row++ = pixel->red * 255;
*row++ = pixel->green * 255;
*row++ = pixel->blue * 255;
}
}
png_init_io (png_ptr, fp);
png_set_rows (png_ptr, info_ptr, row_pointers);
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
for (y = 0; y < bitmap->height; y++)
{
png_free (png_ptr, row_pointers[y]);
}
png_free (png_ptr, row_pointers);
close (fp);
}
else
{
ret = errno;
}
return ret;
}

Handle is invalid

Trying to use SetConsoleScreenBufferSize but it fails and puts "The handle is invalid." In the last error. Will post all code, but here are some highlights:
Using this to resize buffer:
int TGHandleResizeEvent(struct TGHandle *tgHandle, INPUT_RECORD record) {
if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
WINDOW_BUFFER_SIZE_RECORD size = record.Event.WindowBufferSizeEvent;
sizeTGDrawBuffer(&tgHandle->drawBuffer, size.dwSize.X, size.dwSize.Y);
clearTGDrawBuffer(&tgHandle->drawBuffer);
COORD bufferNewSize = {
size.dwSize.X,
size.dwSize.Y
};
return SetConsoleScreenBufferSize(&tgHandle->screenBufferHandle, bufferNewSize);
}
}
Using this to allocate handle:
struct TGHandle TG() {
struct TGHandle tgHandle;
tgHandle.screenBufferHandle = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(tgHandle.screenBufferHandle, &info);
tgHandle.drawBuffer = createTGDrawBuffer(info.dwSize.X, info.dwSize.Y);
// Create the input buffer
tgHandle.inputBufferSize = 32;
tgHandle.inputBuffer = malloc(sizeof(INPUT_RECORD) * tgHandle.inputBufferSize);
// Hook up the input handle
tgHandle.inputHandle = GetStdHandle(STD_INPUT_HANDLE);
return tgHandle;
}
Here is full code.
tg.h
#ifndef TG_H
#define TG_H
#include <Windows.h>
#include <memory.h>
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#define BACKGROUND_WHITE BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
// A drawing buffer, for general purposes
struct TGDrawBuffer {
COORD size;
CHAR_INFO *buffer;
};
struct TGDrawBuffer createTGDrawBuffer(int, int); // Function to allocate a drawing buffer
void sizeTGDrawBuffer(struct TGDrawBuffer*, int, int); // Resize a draw buffer
void clearTGDrawBuffer(struct TGDrawBuffer*); // Fill a buffer with blank cells
void TGDrawPixel(struct TGDrawBuffer*, int, int, CHAR_INFO); // Draw to a single cell on the buffer
void TGDrawAttribute(struct TGDrawBuffer*, int, int, int); // Modify a single attribute. X, Y, Attr
void TGDrawCharInfoString(struct TGDrawBuffer*, int, int, CHAR_INFO*, int); // X, Y, string, int. Draws to max X
CHAR_INFO* TGCharToCharInfo(char*, int); // Convert basic characters to CHAR_INFO. String, length.
void TGDrawString(struct TGDrawBuffer*, int, int, char*, int); // X, Y, string, length. Draws to max X
void freeTGDrawBuffer(struct TGDrawBuffer*); // Function to de-allocate a drawing buffer
int CharInfoStrlen(CHAR_INFO*); // Get length of a CHAR_INFO as if it were a string
// Essentially a drawing context to the screen
struct TGHandle {
HANDLE screenBufferHandle, inputHandle;
struct TGDrawBuffer drawBuffer;
INPUT_RECORD *inputBuffer;
int inputBufferSize;
};
struct TGHandle TG(); // Initialization function, which returns a drawing context to the screen
void useTGHandle(struct TGHandle*); // Make a screen drawing context active
void updateTGHandle(struct TGHandle*); // Displays what has been drawn
void setTGHandleCursorVisibility(struct TGHandle*, int); // True / False
int getTGInput(struct TGHandle*, INPUT_RECORD*, int); // Fill input into a buffer
int getTGNextInput(struct TGHandle*, INPUT_RECORD*); // Get a single INPUT_RECORD or return false
int TGHandleResizeEvent(struct TGHandle*, INPUT_RECORD); // Resize is not handled automatically
#endif
tg.c
#include "tg.h"
#include <string.h>
struct TGDrawBuffer createTGDrawBuffer(int width, int height) {
struct TGDrawBuffer tgDrawBuffer;
tgDrawBuffer.buffer = NULL; // Init the buffer to NULL
sizeTGDrawBuffer(&tgDrawBuffer, width, height);
return tgDrawBuffer;
}
void sizeTGDrawBuffer(struct TGDrawBuffer* drawBuffer, int width, int height) {
// Using free/ malloc here because we aren't interested in retaining data
if (drawBuffer->buffer) {
free(drawBuffer->buffer);
}
drawBuffer->buffer = malloc(sizeof(CHAR_INFO) * (width * height));
// Copy the size to the buffer record
drawBuffer->size.X = width;
drawBuffer->size.Y = height;
}
void clearTGDrawBuffer(struct TGDrawBuffer *tgBuffer) {
int i = 0, limit = tgBuffer->size.X * tgBuffer->size.Y;
// Create a blank CHAR_INFO
CHAR_INFO clearChar;
clearChar.Char.AsciiChar = ' ';
clearChar.Char.UnicodeChar = ' ';
clearChar.Attributes = FOREGROUND_WHITE; // Would be confusing without this
// Set everything to that buffer
while (i < limit) {
tgBuffer->buffer[i] = clearChar;
i++;
}
}
void TGDrawPixel(struct TGDrawBuffer *tgBuffer, int x, int y, CHAR_INFO character) {
tgBuffer->buffer[(tgBuffer->size.X * y) + x] = character;
}
void TGDrawAttribute(struct TGDrawBuffer *tgBuffer, int x, int y, int attr) {
tgBuffer->buffer[(tgBuffer->size.X * y) + x].Attributes = attr;
}
void TGDrawCharInfoString(struct TGDrawBuffer *tgDrawBuffer, int x, int y, CHAR_INFO *string, int length) {
int charsToWrite = length;
int distanceToEnd = (tgDrawBuffer->size.Y - 1) - y;
if (distanceToEnd < charsToWrite)
distanceToEnd = charsToWrite;
int startPos = x + (tgDrawBuffer->size.X * y);
int i = 0;
while (i < distanceToEnd) {
tgDrawBuffer->buffer[startPos + x] = string[i];
i++;
}
}
CHAR_INFO* TGCharToCharInfo(char* string, int length) {
if (length == -1)
length = strlen(string);
// TODO
}
void TGDrawString(struct TGDrawBuffer *tgDrawBuffer, int x, int y, char *string, int length) {
int charsToWrite = length;
int distanceToEnd = (tgDrawBuffer->size.Y - 1) - y;
if (distanceToEnd < charsToWrite)
charsToWrite = distanceToEnd;
int startPos = x + (tgDrawBuffer->size.X * y);
int i = 0;
while (i < charsToWrite) {
tgDrawBuffer->buffer[startPos + i].Char.AsciiChar = string[i];
tgDrawBuffer->buffer[startPos + i].Char.UnicodeChar = string[i];
i++;
}
}
void freeTGDrawBuffer(struct TGDrawBuffer *drawBuffer) {
free(drawBuffer->buffer);
}
struct TGHandle TG() {
struct TGHandle tgHandle;
tgHandle.screenBufferHandle = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(tgHandle.screenBufferHandle, &info);
tgHandle.drawBuffer = createTGDrawBuffer(info.dwSize.X, info.dwSize.Y);
// Create the input buffer
tgHandle.inputBufferSize = 32;
tgHandle.inputBuffer = malloc(sizeof(INPUT_RECORD) * tgHandle.inputBufferSize);
// Hook up the input handle
tgHandle.inputHandle = GetStdHandle(STD_INPUT_HANDLE);
return tgHandle;
}
void useTGHandle(struct TGHandle *tgHandle) {
SetConsoleActiveScreenBuffer(tgHandle->screenBufferHandle);
// Update the buffer sizes
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(tgHandle->screenBufferHandle, &info);
sizeTGDrawBuffer(&tgHandle->drawBuffer, info.dwSize.X, info.dwSize.Y);
clearTGDrawBuffer(&tgHandle->drawBuffer);
}
void updateTGHandle(struct TGHandle *tgHandle) {
COORD size = { tgHandle->drawBuffer.size.X, tgHandle->drawBuffer.size.Y }; // Buffer size
COORD pos = { 0, 0 }; // Start of the buffer coord
SMALL_RECT rect = {
.Left = 0,
.Top = 0,
.Right = size.X - 1,
.Bottom = size.Y - 1
}; // Rect to draw to on destination
WriteConsoleOutput(
tgHandle->screenBufferHandle,
tgHandle->drawBuffer.buffer,
size,
pos,
&rect
);
}
void setTGHandleCursorVisibility(struct TGHandle *tgHandle, int visible) {
// Copy the already-available cursor info
CONSOLE_CURSOR_INFO info;
GetConsoleCursorInfo(tgHandle->screenBufferHandle, &info);
// Modify the cursor visibility
info.bVisible = visible;
SetConsoleCursorInfo(tgHandle->screenBufferHandle, &info);
}
// You should be able to use a TGHandle's input buffer rather than creating your own
// for maximum memory conservation
int getTGInput(struct TGHandle *tgHandle, INPUT_RECORD *inputBuffer, int max) {
int availableRecords;
GetNumberOfConsoleInputEvents(tgHandle->inputHandle, &availableRecords);
int amountToRead = max;
if (availableRecords < max) {
amountToRead = availableRecords;
}
int numberRead;
ReadConsoleInput(
tgHandle->inputHandle,
inputBuffer,
amountToRead,
&numberRead
);
return numberRead;
}
// This function should be pretty performant if someone would not like to use
// the above function and mess around with buffers.
// Input record info: https://learn.microsoft.com/en-us/windows/console/input-record-str
int getTGNextInput(struct TGHandle *tgHandle, INPUT_RECORD *record) {
int availableRecords;
GetNumberOfConsoleInputEvents(tgHandle->inputHandle, &availableRecords);
if (availableRecords == 0) {
return 0;
}
ReadConsoleInput(
tgHandle->inputHandle,
tgHandle->inputBuffer,
1,
&availableRecords
);
*record = tgHandle->inputBuffer[0];
return 1;
}
int TGHandleResizeEvent(struct TGHandle *tgHandle, INPUT_RECORD record) {
if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
WINDOW_BUFFER_SIZE_RECORD size = record.Event.WindowBufferSizeEvent;
sizeTGDrawBuffer(&tgHandle->drawBuffer, size.dwSize.X, size.dwSize.Y);
clearTGDrawBuffer(&tgHandle->drawBuffer);
COORD bufferNewSize = {
size.dwSize.X,
size.dwSize.Y
};
return SetConsoleScreenBufferSize(&tgHandle->screenBufferHandle, bufferNewSize);
}
}
test.c
#include "tg.h"
#include <time.h>
#include <stdio.h>
void getMessageAsStr(char *buf) {
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), 0,
buf, 256, NULL);
}
int main() {
// Error buffer
char buf[256];
// Create a drawing context to the screen
struct TGHandle context = TG();
useTGHandle(&context);
setTGHandleCursorVisibility(&context, 0); // Hide the cursor of course
struct TGDrawBuffer *buffer = &context.drawBuffer;
// Create a CHAR_INFO to draw with
CHAR_INFO info;
info.Attributes = BACKGROUND_BLUE | FOREGROUND_WHITE;
info.Char.AsciiChar = ' ';
info.Char.UnicodeChar = ' ';
INPUT_RECORD input;
const int STRING_BUF_SIZE = 64;
char *fpsCountBuffer = malloc(sizeof(char) * STRING_BUF_SIZE);
long start, end;
start = QueryPerformanceCounter(&start);
int running = 1;
while (running) {
// Start off with a nice clean slate
//clearTGDrawBuffer(buffer);
// Collect input to react to resize
while (getTGNextInput(&context, &input)) {
if (input.EventType == WINDOW_BUFFER_SIZE_EVENT) {
if (!TGHandleResizeEvent(&context, input)) {
OutputDebugString("Couldn't resize:\n");
getMessageAsStr(buf);
OutputDebugString(buf);
}
}
}
// Draw line along top and bottom
int i = 0;
while (i < buffer->size.X) {
TGDrawPixel(buffer, i, 0, info);
TGDrawPixel(buffer, i, buffer->size.Y - 1, info);
i++;
}
i = 0;
// Draw vertical lines
while (i < buffer->size.Y) {
TGDrawPixel(buffer, 0, i, info);
TGDrawPixel(buffer, buffer->size.X - 1, i, info);
i++;
}
// FPS count!
// Get time elapsed in millis
QueryPerformanceCounter(&end);
long fps = 1000000 / (end - start);
// Put it into the screen buffer
snprintf(fpsCountBuffer, STRING_BUF_SIZE, "Running at %ldhz, %dx%d", fps, buffer->size.X, buffer->size.Y);
TGDrawString(buffer, 1, 1, fpsCountBuffer, strlen(fpsCountBuffer));
start = end;
updateTGHandle(&context);
}
}
Spotted it right after I posted it. You can see that I'm taking the pointer location of the handle in TGHandleResizeEvent:
return SetConsoleScreenBufferSize(&tgHandle->screenBufferHandle, bufferNewSize);
This is, in fact, an invalid handle. Corrected code:
return SetConsoleScreenBufferSize(tgHandle->screenBufferHandle, bufferNewSize);

Load an image to a GSL matrix

Does anyone know about some function that loads a grayscale image into a GSL matrix?
Something like:
gsl_matrix *M;
load_image("./image.jpg", M); // any image extension would also be fine
Something like this works on my PC: allows you to load a grayscale JPG image using libjpeg.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jpeglib.h>
#include <gsl/gsl_matrix.h>
gsl_matrix *load_jpg_image(const char *pFileName)
{
FILE *pFile;
long jpegSize;
unsigned char *pJpegBytes, *pPixels;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
int status, w, h, numComponents, stride, x, y;
gsl_matrix *pMatrix;
pFile = fopen(pFileName, "rb");
if (!pFile)
{
fprintf(stderr, "Can't open file\n");
return NULL;
}
// Get the size of the file
fseek(pFile, 0, SEEK_END);
jpegSize = ftell(pFile);
rewind(pFile);
if (jpegSize == 0)
{
fclose(pFile);
fprintf(stderr, "Empty file\n");
return NULL;
}
// Read it into memory and close file
pJpegBytes = (unsigned char *)malloc(jpegSize);
fread(pJpegBytes, 1, jpegSize, pFile);
fclose(pFile);
// Jpeg decompression starts here
memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct));
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, pJpegBytes, jpegSize);
status = jpeg_read_header(&cinfo, TRUE);
if (status != 1)
{
free(pJpegBytes);
fprintf(stderr, "Invalid JPEG header\n");
return NULL;
}
jpeg_start_decompress(&cinfo);
w = cinfo.output_width;
h = cinfo.output_height;
numComponents = cinfo.output_components;
if (numComponents != 1)
{
free(pJpegBytes);
fprintf(stderr, "Can only handle 1 color component\n");
return NULL;
}
pPixels = (unsigned char *)malloc(w*h);
stride = w*numComponents;
// perhaps this can de done much faster by processing
// multiple lines at once
while (cinfo.output_scanline < cinfo.output_height)
{
unsigned char *buffer_array[1];
buffer_array[0] = pPixels + cinfo.output_scanline * stride;
jpeg_read_scanlines(&cinfo, buffer_array, 1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
free(pJpegBytes);
// Now, create and fill in the matrix
pMatrix = gsl_matrix_alloc(h, w);
for (y = 0 ; y < h ; y++)
for (x = 0 ; x < w ; x++)
gsl_matrix_set(pMatrix, y, x, pPixels[x+y*stride]);
return pMatrix;
}
int main(void)
{
gsl_matrix *pMatrix;
int rows, cols;
int i, j;
pMatrix = load_jpg_image("test.jpg");
if (pMatrix == NULL)
{
fprintf(stderr, "Can't load matrix\n");
return -1;
}
//
// Use the matrix
//
gsl_matrix_free(pMatrix);
return 0;
}

How to decode a png image to raw bytes from C code with libpng?

I'm looking for a way to decode some png file, I heard about libpng, but I don't understand how this one works. Does it convert the png file into an array of bytes in the ARGB8888 format or something else ?
Runnable example
This example reads and existing PNG, modifies it, and writes a modified version to disk.
The modification part is done on raw bytes.
Usage:
./a.out [<old-png> [<new-png>]]
old-png defaults to a.png
new-png defaults to b.png
Tested on Ubuntu 18.04, libpng 1.6.34, compile with:
gcc -std=c99 main.c -lpng
Adapted from: https://gist.github.com/niw/5963798
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
unsigned int width;
unsigned int height;
png_bytep *row_pointers;
static void read_png_file(char *filename) {
FILE *fp = fopen(filename, "rb");
png_byte bit_depth;
png_byte color_type;
unsigned int y;
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) abort();
png_infop info = png_create_info_struct(png);
if (!info) abort();
if (setjmp(png_jmpbuf(png))) abort();
png_init_io(png, fp);
png_read_info(png, info);
width = png_get_image_width(png, info);
height = png_get_image_height(png, info);
color_type = png_get_color_type(png, info);
bit_depth = png_get_bit_depth(png, info);
/* Read any color_type into 8bit depth, RGBA format. */
/* See http://www.libpng.org/pub/png/libpng-manual.txt */
if (bit_depth == 16)
png_set_strip_16(png);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
/* PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png);
/* These color_type don't have an alpha channel then fill it with 0xff. */
if (color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_read_update_info(png, info);
row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
for (y = 0; y < height; y++) {
row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png,info));
}
png_read_image(png, row_pointers);
fclose(fp);
}
static void write_png_file(char *filename) {
unsigned int y;
FILE *fp = fopen(filename, "wb");
if (!fp) abort();
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) abort();
png_infop info = png_create_info_struct(png);
if (!info) abort();
if (setjmp(png_jmpbuf(png))) abort();
png_init_io(png, fp);
png_set_IHDR(
png,
info,
width,
height,
8,
PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT
);
png_write_info(png, info);
/* To remove the alpha channel for PNG_COLOR_TYPE_RGB format, */
/* Use png_set_filler(). */
/*png_set_filler(png, 0, PNG_FILLER_AFTER);*/
png_write_image(png, row_pointers);
png_write_end(png, NULL);
for (y = 0; y < height; y++) {
free(row_pointers[y]);
}
free(row_pointers);
fclose(fp);
}
static void process_png(void) {
for (unsigned int y = 0; y < height; y++) {
png_bytep row = row_pointers[y];
for (unsigned int x = 0; x < width; x++) {
png_bytep px = &(row[x * 4]);
/*printf("%4d, %4d = RGBA(%3d, %3d, %3d, %3d)\n", x, y, px[0], px[1], px[2], px[3]);*/
png_byte old[4 * sizeof(png_byte)];
memcpy(old, px, sizeof(old));
px[0] = 255 - old[0];
px[1] = 255 - old[1];
px[2] = 255 - old[2];
}
}
}
int main(int argc, char *argv[]) {
char *in;
char *out;
if (argc > 1) {
in = argv[1];
} else {
in = "a.png";
}
if (argc > 2) {
out = argv[2];
} else {
out = "b.png";
}
read_png_file(in);
process_png();
write_png_file(out);
return EXIT_SUCCESS;
}
a.png
b.png
Image source: https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Catherine_of_Aragon.png/430px-Catherine_of_Aragon.png
You might be better off looking at a dedicated image library that will decode the image for you and return it in a recognised structure. It'll also serve as a better platform when you want to actually do something with the image (saving, displaying, etc).

Resources