#include #include #include #include #include #include #include #include #include #include "jpg.h" /* * Checks if the file is a JPEG by looking for a valid JPEG header. */ bool file_is_jpg(char* file_path) { if (!file_path) return false; FILE* image_file; uint16_t file_header; size_t read_count; // TODO: Consider endianess on non-x86 platforms uint16_t jpg_magick = 0xd8ff; image_file = fopen(file_path, "rb"); if (image_file == NULL) { int img_err = errno; fprintf(stderr, "Could not open image file %s: %s\n", file_path, strerror(img_err)); return false; } read_count = fread(&file_header, sizeof(file_header), 1, image_file); fclose(image_file); if (read_count < 1) { fprintf(stderr, "Error searching for JPEG header in %s\n", file_path); return false; } return file_header == jpg_magick; } /* * Reads a JPEG from a file into memory, in a format that Cairo can create a * surface from. */ void* read_JPEG_file(char *file_path, JPEG_INFO *jpg_info) { int img_err; struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; FILE *infile; /* source file */ void *img; /* decompressed image data pointer */ if ((infile = fopen(file_path, "rb")) == NULL) { img_err = errno; fprintf(stderr, "Could not open image file %s: %s\n", file_path, strerror(img_err)); return NULL; } cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); (void) jpeg_read_header(&cinfo, TRUE); // BGRA + endianness change = RGBA? // TODO: Test this code on non-x86_64 platforms cinfo.out_color_space = JCS_EXT_BGRA; (void) jpeg_start_decompress(&cinfo); jpg_info->height = cinfo.output_height; jpg_info->width = cinfo.output_width; /* Get the *cairo* stride rather than the stride from the image. This is * the space needed in memory for each row for optimized Cairo rendering. */ int cairo_stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, cinfo.output_width); jpg_info->stride = cairo_stride; if (cairo_stride < jpg_info->width) { /* This should never happen, but if it does then the following code * will potentially write into unallocated memory */ fprintf( stderr, "WARNING: Cairo stride shorter than JPEG width. Aborting JPEG read." ); return NULL; } // Allocate storage for the final, decompressed image. img = calloc(cairo_stride, cinfo.output_height); if (img == NULL) { fprintf(stderr, "Could not allocate memory for JPEG decode\n"); (void) jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); return NULL; } while (cinfo.output_scanline < cinfo.output_height) { /* Normally, you would allocate a buffer using libJPEG's memory * management and write into it, but since we're reading one row at a * time, we just write it directly into the image memory space */ unsigned char* pos = img + (cairo_stride * (cinfo.output_scanline)); (void) jpeg_read_scanlines(&cinfo, &pos, 1); } (void) jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); return img; }