/* * JPEG image routines for CUPS. * * Copyright 2007-2011 by Apple Inc. * Copyright 1993-2007 by Easy Software Products. * * These coded instructions, statements, and computer programs are the * property of Apple Inc. and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "COPYING" * which should have been included with this file. * * Contents: * * _cupsImageReadJPEG() - Read a JPEG image file. */ /* * Include necessary headers... */ #include "image-private.h" #ifdef HAVE_LIBJPEG # include /* JPEG/JFIF image definitions */ #define JPEG_APP0 0xE0 /* APP0 marker code */ /* * '_cupsImageReadJPEG()' - Read a JPEG image file. */ int /* O - Read status */ _cupsImageReadJPEG( cups_image_t *img, /* IO - cupsImage */ FILE *fp, /* I - cupsImage file */ cups_icspace_t primary, /* I - Primary choice for colorspace */ cups_icspace_t secondary, /* I - Secondary choice for colorspace */ int saturation, /* I - Color saturation (%) */ int hue, /* I - Color hue (degrees) */ const cups_ib_t *lut) /* I - Lookup table for gamma/brightness */ { struct jpeg_decompress_struct cinfo; /* Decompressor info */ struct jpeg_error_mgr jerr; /* Error handler info */ cups_ib_t *in, /* Input pixels */ *out; /* Output pixels */ jpeg_saved_marker_ptr marker; /* Pointer to marker data */ int psjpeg = 0; /* Non-zero if Photoshop CMYK JPEG */ static const char *cspaces[] = { /* JPEG colorspaces... */ "JCS_UNKNOWN", "JCS_GRAYSCALE", "JCS_RGB", "JCS_YCbCr", "JCS_CMYK", "JCS_YCCK" }; /* * Read the JPEG header... */ cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); jpeg_save_markers(&cinfo, JPEG_APP0 + 14, 0xffff); /* Adobe JPEG */ jpeg_stdio_src(&cinfo, fp); jpeg_read_header(&cinfo, 1); /* * Parse any Adobe APPE data embedded in the JPEG file. Since Adobe doesn't * bother following standards, we have to invert the CMYK JPEG data written by * Adobe apps... */ for (marker = cinfo.marker_list; marker; marker = marker->next) if (marker->marker == (JPEG_APP0 + 14) && marker->data_length >= 12 && !memcmp(marker->data, "Adobe", 5)) { fputs("DEBUG: Adobe CMYK JPEG detected (inverting color values)\n", stderr); psjpeg = 1; } cinfo.quantize_colors = 0; fprintf(stderr, "DEBUG: num_components = %d\n", cinfo.num_components); fprintf(stderr, "DEBUG: jpeg_color_space = %s\n", cspaces[cinfo.jpeg_color_space]); if (cinfo.num_components == 1) { fputs("DEBUG: Converting image to grayscale...\n", stderr); cinfo.out_color_space = JCS_GRAYSCALE; cinfo.out_color_components = 1; cinfo.output_components = 1; img->colorspace = secondary; } else if (cinfo.num_components == 4) { fputs("DEBUG: Converting image to CMYK...\n", stderr); cinfo.out_color_space = JCS_CMYK; cinfo.out_color_components = 4; cinfo.output_components = 4; img->colorspace = (primary == CUPS_IMAGE_RGB_CMYK) ? CUPS_IMAGE_CMYK : primary; } else { fputs("DEBUG: Converting image to RGB...\n", stderr); cinfo.out_color_space = JCS_RGB; cinfo.out_color_components = 3; cinfo.output_components = 3; img->colorspace = (primary == CUPS_IMAGE_RGB_CMYK) ? CUPS_IMAGE_RGB : primary; } jpeg_calc_output_dimensions(&cinfo); if (cinfo.output_width <= 0 || cinfo.output_width > CUPS_IMAGE_MAX_WIDTH || cinfo.output_height <= 0 || cinfo.output_height > CUPS_IMAGE_MAX_HEIGHT) { fprintf(stderr, "DEBUG: Bad JPEG dimensions %dx%d!\n", cinfo.output_width, cinfo.output_height); jpeg_destroy_decompress(&cinfo); fclose(fp); return (1); } img->xsize = cinfo.output_width; img->ysize = cinfo.output_height; int temp = -1; #ifdef HAVE_EXIF /* scan image file for exif data */ temp = _cupsImageReadEXIF(img, fp); #endif /* check headers only if EXIF contains no info about ppi */ if (temp != 1 && cinfo.X_density > 0 && cinfo.Y_density > 0 && cinfo.density_unit > 0) { if (cinfo.density_unit == 1) { img->xppi = cinfo.X_density; img->yppi = cinfo.Y_density; } else { img->xppi = (int)((float)cinfo.X_density * 2.54); img->yppi = (int)((float)cinfo.Y_density * 2.54); } if (img->xppi == 0 || img->yppi == 0) { fprintf(stderr, "DEBUG: Bad JPEG image resolution %dx%d PPI.\n", img->xppi, img->yppi); img->xppi = img->yppi = 200; } } fprintf(stderr, "DEBUG: JPEG image %dx%dx%d, %dx%d PPI\n", img->xsize, img->ysize, cinfo.output_components, img->xppi, img->yppi); cupsImageSetMaxTiles(img, 0); in = malloc(img->xsize * cinfo.output_components); out = malloc(img->xsize * cupsImageGetDepth(img)); jpeg_start_decompress(&cinfo); while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, (JSAMPROW *)&in, (JDIMENSION)1); if (psjpeg && cinfo.output_components == 4) { /* * Invert CMYK data from Photoshop... */ cups_ib_t *ptr; /* Pointer into buffer */ int i; /* Looping var */ for (ptr = in, i = img->xsize * 4; i > 0; i --, ptr ++) *ptr = 255 - *ptr; } if ((saturation != 100 || hue != 0) && cinfo.output_components == 3) cupsImageRGBAdjust(in, img->xsize, saturation, hue); if ((img->colorspace == CUPS_IMAGE_WHITE && cinfo.out_color_space == JCS_GRAYSCALE) || (img->colorspace == CUPS_IMAGE_CMYK && cinfo.out_color_space == JCS_CMYK)) { #ifdef DEBUG int i, j; cups_ib_t *ptr; fputs("DEBUG: Direct Data...\n", stderr); fputs("DEBUG:", stderr); for (i = 0, ptr = in; i < img->xsize; i ++) { putc(' ', stderr); for (j = 0; j < cinfo.output_components; j ++, ptr ++) fprintf(stderr, "%02X", *ptr & 255); } putc('\n', stderr); #endif /* DEBUG */ if (lut) cupsImageLut(in, img->xsize * cupsImageGetDepth(img), lut); _cupsImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, in); } else if (cinfo.out_color_space == JCS_GRAYSCALE) { switch (img->colorspace) { default : break; case CUPS_IMAGE_BLACK : cupsImageWhiteToBlack(in, out, img->xsize); break; case CUPS_IMAGE_RGB : cupsImageWhiteToRGB(in, out, img->xsize); break; case CUPS_IMAGE_CMY : cupsImageWhiteToCMY(in, out, img->xsize); break; case CUPS_IMAGE_CMYK : cupsImageWhiteToCMYK(in, out, img->xsize); break; } if (lut) cupsImageLut(out, img->xsize * cupsImageGetDepth(img), lut); _cupsImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, out); } else if (cinfo.out_color_space == JCS_RGB) { switch (img->colorspace) { default : break; case CUPS_IMAGE_RGB : cupsImageRGBToRGB(in, out, img->xsize); break; case CUPS_IMAGE_WHITE : cupsImageRGBToWhite(in, out, img->xsize); break; case CUPS_IMAGE_BLACK : cupsImageRGBToBlack(in, out, img->xsize); break; case CUPS_IMAGE_CMY : cupsImageRGBToCMY(in, out, img->xsize); break; case CUPS_IMAGE_CMYK : cupsImageRGBToCMYK(in, out, img->xsize); break; } if (lut) cupsImageLut(out, img->xsize * cupsImageGetDepth(img), lut); _cupsImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, out); } else /* JCS_CMYK */ { fputs("DEBUG: JCS_CMYK\n", stderr); switch (img->colorspace) { default : break; case CUPS_IMAGE_WHITE : cupsImageCMYKToWhite(in, out, img->xsize); break; case CUPS_IMAGE_BLACK : cupsImageCMYKToBlack(in, out, img->xsize); break; case CUPS_IMAGE_CMY : cupsImageCMYKToCMY(in, out, img->xsize); break; case CUPS_IMAGE_RGB : cupsImageCMYKToRGB(in, out, img->xsize); break; } if (lut) cupsImageLut(out, img->xsize * cupsImageGetDepth(img), lut); _cupsImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, out); } } free(in); free(out); jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(fp); return (0); } #endif /* HAVE_LIBJPEG */