blob: dcefb4145039a996f39158c2cf950c3ebce3c80c [file] [log] [blame]
// Copyright 2020 Joe Drago. All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
#include "avifjpeg.h"
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jpeglib.h"
#include "iccjpeg.h"
// This warning triggers false postives way too often in here.
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wclobbered"
#endif
struct my_error_mgr
{
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr * my_error_ptr;
static void my_error_exit(j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr)cinfo->err;
(*cinfo->err->output_message)(cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
avifBool avifJPEGRead(avifImage * avif, const char * inputFilename, avifPixelFormat requestedFormat, int requestedDepth)
{
avifBool ret = AVIF_FALSE;
FILE * f = NULL;
uint8_t * iccData = NULL;
avifRGBImage rgb;
memset(&rgb, 0, sizeof(avifRGBImage));
struct my_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
return AVIF_FALSE;
}
jpeg_create_decompress(&cinfo);
f = fopen(inputFilename, "rb");
if (!f) {
fprintf(stderr, "Can't open JPEG file for read: %s\n", inputFilename);
goto cleanup;
}
setup_read_icc_profile(&cinfo);
jpeg_stdio_src(&cinfo, f);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
int row_stride = cinfo.output_width * cinfo.output_components;
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
unsigned int iccDataLen;
if (read_icc_profile(&cinfo, &iccData, &iccDataLen)) {
if (avif->profileFormat == AVIF_PROFILE_FORMAT_NONE) {
avifImageSetProfileICC(avif, iccData, (size_t)iccDataLen);
} else {
fprintf(stderr, "WARNING: JPEG contains ICC profile which is being overridden with --nclx\n");
}
}
avif->width = cinfo.output_width;
avif->height = cinfo.output_height;
avif->yuvFormat = requestedFormat;
avif->depth = requestedDepth ? requestedDepth : 8;
avifRGBImageSetDefaults(&rgb, avif);
rgb.format = AVIF_RGB_FORMAT_RGB;
rgb.depth = 8;
avifRGBImageAllocatePixels(&rgb);
int row = 0;
while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, buffer, 1);
uint8_t * pixelRow = &rgb.pixels[row * rgb.rowBytes];
memcpy(pixelRow, buffer[0], rgb.rowBytes);
++row;
}
avifImageRGBToYUV(avif, &rgb);
jpeg_finish_decompress(&cinfo);
ret = AVIF_TRUE;
cleanup:
jpeg_destroy_decompress(&cinfo);
if (f) {
fclose(f);
}
free(iccData);
avifRGBImageFreePixels(&rgb);
return ret;
}
avifBool avifJPEGWrite(avifImage * avif, const char * outputFilename, int jpegQuality)
{
avifBool ret = AVIF_FALSE;
FILE * f = NULL;
(void)avif;
(void)outputFilename;
avifRGBImage rgb;
avifRGBImageSetDefaults(&rgb, avif);
rgb.format = AVIF_RGB_FORMAT_RGB;
rgb.depth = 8;
avifRGBImageAllocatePixels(&rgb);
avifImageYUVToRGB(avif, &rgb);
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1];
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
f = fopen(outputFilename, "wb");
if (!f) {
fprintf(stderr, "Can't open PNG file for write: %s\n", outputFilename);
goto cleanup;
}
jpeg_stdio_dest(&cinfo, f);
cinfo.image_width = avif->width;
cinfo.image_height = avif->height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, jpegQuality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
if (avif->profileFormat == AVIF_PROFILE_FORMAT_ICC) {
write_icc_profile(&cinfo, avif->icc.data, (unsigned int)avif->icc.size);
}
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = &rgb.pixels[cinfo.next_scanline * rgb.rowBytes];
(void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
ret = AVIF_TRUE;
printf("Wrote JPEG: %s\n", outputFilename);
cleanup:
if (f) {
fclose(f);
}
jpeg_destroy_compress(&cinfo);
avifRGBImageFreePixels(&rgb);
return ret;
}