static ColorProfile readColorProfile(jpeg_decompress_struct* info) { #if USE(ICCJPEG) JOCTET* profile; unsigned int profileLength; if (!read_icc_profile(info, &profile, &profileLength)) return ColorProfile(); // Only accept RGB color profiles from input class devices. bool ignoreProfile = false; char* profileData = reinterpret_cast<char*>(profile); if (profileLength < ImageDecoder::iccColorProfileHeaderLength) ignoreProfile = true; else if (!ImageDecoder::rgbColorProfile(profileData, profileLength)) ignoreProfile = true; else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileLength)) ignoreProfile = true; ColorProfile colorProfile; if (!ignoreProfile) colorProfile.append(profileData, profileLength); free(profile); return colorProfile; #else UNUSED_PARAM(info); return ColorProfile(); #endif }
//- JPEG ----------------------------------------------------------------------- static cmsHPROFILE loadFromJpegData(const QByteArray& data) { cmsHPROFILE profile = 0; struct jpeg_decompress_struct srcinfo; JPEGErrorManager srcErrorManager; srcinfo.err = &srcErrorManager; jpeg_create_decompress(&srcinfo); if (setjmp(srcErrorManager.jmp_buffer)) { kError() << "libjpeg error in src\n"; return 0; } QBuffer buffer(const_cast<QByteArray*>(&data)); buffer.open(QIODevice::ReadOnly); IODeviceJpegSourceManager::setup(&srcinfo, &buffer); setup_read_icc_profile(&srcinfo); jpeg_read_header(&srcinfo, true); jpeg_start_decompress(&srcinfo); uchar* profile_data; uint profile_len; if (read_icc_profile(&srcinfo, &profile_data, &profile_len)) { LOG("Found a profile, length:" << profile_len); profile = cmsOpenProfileFromMem(profile_data, profile_len); } jpeg_destroy_decompress(&srcinfo); return profile; }
static void readColorProfile(jpeg_decompress_struct* info, ColorProfile& colorProfile) { #if USE(ICCJPEG) JOCTET* profile; unsigned profileLength; if (!read_icc_profile(info, &profile, &profileLength)) return; // Only accept RGB color profiles from input class devices. bool ignoreProfile = false; char* profileData = reinterpret_cast<char*>(profile); if (profileLength < ImageDecoder::iccColorProfileHeaderLength) ignoreProfile = true; else if (!ImageDecoder::rgbColorProfile(profileData, profileLength)) ignoreProfile = true; else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileLength)) ignoreProfile = true; ASSERT(colorProfile.isEmpty()); if (!ignoreProfile) colorProfile.append(profileData, profileLength); free(profile); #endif }
static ColorProfile readColorProfile(jpeg_decompress_struct* info) { #if USE(ICCJPEG) JOCTET* profile; unsigned int profileLength; if (!read_icc_profile(info, &profile, &profileLength)) return ColorProfile(); char* profileData = reinterpret_cast<char*>(profile); // Images with grayscale profiles get "upsampled" by libjpeg. If we use // their color profile, CoreGraphics will "upsample" them // again, resulting in horizontal distortions. if (profileLength >= 20 && !memcmp(&profileData[16], "GRAY", 4)) { free(profile); return ColorProfile(); } ColorProfile colorProfile; colorProfile.append(profileData, profileLength); free(profile); return colorProfile; #else return ColorProfile(); #endif }
int dt_imageio_jpeg_read_profile(dt_imageio_jpeg_t *jpg, uint8_t **out) { unsigned int length = 0; boolean res = read_icc_profile(&(jpg->dinfo), out, &length); jpeg_destroy_decompress(&(jpg->dinfo)); fclose(jpg->f); return res?length:0; }
static ColorProfile readColorProfile(jpeg_decompress_struct* info) { #if USE(ICCJPEG) JOCTET* profile; unsigned int profileLength; if (!read_icc_profile(info, &profile, &profileLength)) return ColorProfile(); ColorProfile colorProfile; colorProfile.append(reinterpret_cast<char*>(profile), profileLength); free(profile); return colorProfile; #else return ColorProfile(); #endif }
bool JpgInput::open (const std::string &name, ImageSpec &newspec) { // Check that file exists and can be opened m_filename = name; m_fd = Filesystem::fopen (name, "rb"); if (m_fd == NULL) { error ("Could not open file \"%s\"", name.c_str()); return false; } // Check magic number to assure this is a JPEG file uint8_t magic[2] = {0, 0}; if (fread (magic, sizeof(magic), 1, m_fd) != 1) { error ("Empty file \"%s\"", name.c_str()); close_file (); return false; } rewind (m_fd); if (magic[0] != JPEG_MAGIC1 || magic[1] != JPEG_MAGIC2) { close_file (); error ("\"%s\" is not a JPEG file, magic number doesn't match (was 0x%x%x)", name.c_str(), int(magic[0]), int(magic[1])); return false; } // Set up the normal JPEG error routines, then override error_exit and // output_message so we intercept all the errors. m_cinfo.err = jpeg_std_error ((jpeg_error_mgr *)&m_jerr); m_jerr.pub.error_exit = my_error_exit; m_jerr.pub.output_message = my_output_message; if (setjmp (m_jerr.setjmp_buffer)) { // Jump to here if there's a libjpeg internal error // Prevent memory leaks, see example.c in jpeg distribution jpeg_destroy_decompress (&m_cinfo); close_file (); return false; } jpeg_create_decompress (&m_cinfo); // initialize decompressor jpeg_stdio_src (&m_cinfo, m_fd); // specify the data source // Request saving of EXIF and other special tags for later spelunking for (int mark = 0; mark < 16; ++mark) jpeg_save_markers (&m_cinfo, JPEG_APP0+mark, 0xffff); jpeg_save_markers (&m_cinfo, JPEG_COM, 0xffff); // comment marker // read the file parameters if (jpeg_read_header (&m_cinfo, FALSE) != JPEG_HEADER_OK || m_fatalerr) { error ("Bad JPEG header for \"%s\"", filename().c_str()); return false; } int nchannels = m_cinfo.num_components; if (m_cinfo.jpeg_color_space == JCS_CMYK || m_cinfo.jpeg_color_space == JCS_YCCK) { // CMYK jpegs get converted by us to RGB m_cinfo.out_color_space = JCS_CMYK; // pre-convert YCbCrK->CMYK nchannels = 3; m_cmyk = true; } if (m_raw) m_coeffs = jpeg_read_coefficients (&m_cinfo); else jpeg_start_decompress (&m_cinfo); // start working if (m_fatalerr) return false; m_next_scanline = 0; // next scanline we'll read m_spec = ImageSpec (m_cinfo.output_width, m_cinfo.output_height, nchannels, TypeDesc::UINT8); // Assume JPEG is in sRGB unless the Exif or XMP tags say otherwise. m_spec.attribute ("oiio:ColorSpace", "sRGB"); if (m_cinfo.jpeg_color_space == JCS_CMYK) m_spec.attribute ("jpeg:ColorSpace", "CMYK"); else if (m_cinfo.jpeg_color_space == JCS_YCCK) m_spec.attribute ("jpeg:ColorSpace", "YCbCrK"); // If the chroma subsampling is detected and matches something // we expect, then set an attribute so that it can be preserved // in future operations. std::string subsampling = comp_info_to_attr(m_cinfo); if (!subsampling.empty()) m_spec.attribute(JPEG_SUBSAMPLING_ATTR, subsampling); for (jpeg_saved_marker_ptr m = m_cinfo.marker_list; m; m = m->next) { if (m->marker == (JPEG_APP0+1) && ! strcmp ((const char *)m->data, "Exif")) { // The block starts with "Exif\0\0", so skip 6 bytes to get // to the start of the actual Exif data TIFF directory decode_exif ((unsigned char *)m->data+6, m->data_length-6, m_spec); } else if (m->marker == (JPEG_APP0+1) && ! strcmp ((const char *)m->data, "http://ns.adobe.com/xap/1.0/")) { #ifndef NDEBUG std::cerr << "Found APP1 XMP! length " << m->data_length << "\n"; #endif std::string xml ((const char *)m->data, m->data_length); decode_xmp (xml, m_spec); } else if (m->marker == (JPEG_APP0+13) && ! strcmp ((const char *)m->data, "Photoshop 3.0")) jpeg_decode_iptc ((unsigned char *)m->data); else if (m->marker == JPEG_COM) { if (! m_spec.find_attribute ("ImageDescription", TypeDesc::STRING)) m_spec.attribute ("ImageDescription", std::string ((const char *)m->data, m->data_length)); } } // Handle density/pixelaspect. We need to do this AFTER the exif is // decoded, in case it contains useful information. float xdensity = m_spec.get_float_attribute ("XResolution"); float ydensity = m_spec.get_float_attribute ("YResolution"); if (! xdensity || ! ydensity) { xdensity = float(m_cinfo.X_density); ydensity = float(m_cinfo.Y_density); if (xdensity && ydensity) { m_spec.attribute ("XResolution", xdensity); m_spec.attribute ("YResolution", ydensity); } } if (xdensity && ydensity) { float aspect = ydensity/xdensity; if (aspect != 1.0f) m_spec.attribute ("PixelAspectRatio", aspect); switch (m_cinfo.density_unit) { case 0 : m_spec.attribute ("ResolutionUnit", "none"); break; case 1 : m_spec.attribute ("ResolutionUnit", "in"); break; case 2 : m_spec.attribute ("ResolutionUnit", "cm"); break; } } read_icc_profile(&m_cinfo, m_spec); /// try to read icc profile newspec = m_spec; return true; }
static int TransformImage(char *cDefInpProf, char *cOutProf) { cmsHPROFILE hIn, hOut, hProof; cmsHTRANSFORM xform; cmsUInt32Number wInput, wOutput; int OutputColorSpace; cmsUInt32Number dwFlags = 0; cmsUInt32Number EmbedLen; cmsUInt8Number* EmbedBuffer; cmsSetAdaptationState(ObserverAdaptationState); if (BlackPointCompensation) { dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } switch (PrecalcMode) { case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break; case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break; case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break; default:; } if (GamutCheck) { dwFlags |= cmsFLAGS_GAMUTCHECK; cmsSetAlarmCodes(Alarm); } // Take input color space wInput = GetInputPixelType(); if (lIsDeviceLink) { hIn = cmsOpenProfileFromFile(cDefInpProf, "r"); hOut = NULL; hProof = NULL; } else { if (!IgnoreEmbedded && read_icc_profile(&Decompressor, &EmbedBuffer, &EmbedLen)) { hIn = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen); if (Verbose) { fprintf(stdout, " (Embedded profile found)\n"); PrintProfileInformation(hIn); fflush(stdout); } if (hIn != NULL && SaveEmbedded != NULL) SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded); free(EmbedBuffer); } else { // Default for ITU/Fax if (cDefInpProf == NULL && T_COLORSPACE(wInput) == PT_Lab) cDefInpProf = "*Lab"; if (cDefInpProf != NULL && cmsstrcasecmp(cDefInpProf, "*lab") == 0) hIn = CreateITU2PCS_ICC(); else hIn = OpenStockProfile(0, cDefInpProf); } if (cOutProf != NULL && cmsstrcasecmp(cOutProf, "*lab") == 0) hOut = CreatePCS2ITU_ICC(); else hOut = OpenStockProfile(0, cOutProf); hProof = NULL; if (cProofing != NULL) { hProof = OpenStockProfile(0, cProofing); if (hProof == NULL) { FatalError("Proofing profile couldn't be read."); } dwFlags |= cmsFLAGS_SOFTPROOFING; } } if (!hIn) FatalError("Input profile couldn't be read."); if (!hOut) FatalError("Output profile couldn't be read."); // Assure both, input profile and input JPEG are on same colorspace if (cmsGetColorSpace(hIn) != _cmsICCcolorSpace(T_COLORSPACE(wInput))) FatalError("Input profile is not operating in proper color space"); // Output colorspace is given by output profile if (lIsDeviceLink) { OutputColorSpace = GetDevicelinkColorSpace(hIn); } else { OutputColorSpace = GetProfileColorSpace(hOut); } jpeg_copy_critical_parameters(&Decompressor, &Compressor); WriteOutputFields(OutputColorSpace); wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace); xform = cmsCreateProofingTransform(hIn, wInput, hOut, wOutput, hProof, Intent, ProofingIntent, dwFlags); if (xform == NULL) FatalError("Cannot transform by using the profiles"); DoTransform(xform, OutputColorSpace); jcopy_markers_execute(&Decompressor, &Compressor); cmsDeleteTransform(xform); cmsCloseProfile(hIn); cmsCloseProfile(hOut); if (hProof) cmsCloseProfile(hProof); return 1; }
static int TransformImage(char *cDefInpProf, char *cOutProf) { cmsHPROFILE hIn, hOut, hProof; cmsHTRANSFORM xform; DWORD wInput, wOutput; int OutputColorSpace; DWORD dwFlags = 0; DWORD EmbedLen; LPBYTE EmbedBuffer; if (BlackPointCompensation) { dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } switch (PrecalcMode) { case 0: dwFlags |= cmsFLAGS_NOTPRECALC; break; case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break; case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break; default:; } if (GamutCheck) dwFlags |= cmsFLAGS_GAMUTCHECK; if (lIsDeviceLink) { hIn = cmsOpenProfileFromFile(cDefInpProf, "r"); hOut = NULL; hProof = NULL; } else { if (!IgnoreEmbedded && read_icc_profile(&Decompressor, &EmbedBuffer, &EmbedLen)) { hIn = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen); if (Verbose) { fprintf(stdout, " (Embedded profile found)\n"); fprintf(stdout, "Product name: %s\n", cmsTakeProductName(hIn)); fprintf(stdout, "Description : %s\n", cmsTakeProductDesc(hIn)); fflush(stdout); } if (hIn != NULL && SaveEmbedded != NULL) SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded); free(EmbedBuffer); } else { hIn = OpenStockProfile(cDefInpProf); } hOut = OpenStockProfile(cOutProf); hProof = NULL; if (cProofing != NULL) { hProof = OpenStockProfile(cProofing); } } // Take input color space wInput = GetInputPixelType(); // Assure both, input profile and input JPEG are on same colorspace if (cmsGetColorSpace(hIn) != _cmsICCcolorSpace(T_COLORSPACE(wInput))) FatalError("Input profile is not operating in proper color space"); // Output colorspace is given by output profile if (lIsDeviceLink) { OutputColorSpace = T_COLORSPACE(wInput); } else { OutputColorSpace = GetProfileColorSpace(hOut); } jpeg_copy_critical_parameters(&Decompressor, &Compressor); WriteOutputFields(OutputColorSpace); wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace); xform = cmsCreateProofingTransform(hIn, wInput, hOut, wOutput, hProof, Intent, ProofingIntent, dwFlags); // Handle tile by tile or strip by strip strtok DoTransform(xform); jcopy_markers_execute(&Decompressor, &Compressor); cmsDeleteTransform(xform); cmsCloseProfile(hIn); cmsCloseProfile(hOut); if (hProof) cmsCloseProfile(hProof); return 1; }
static gint32 load_image (char *filename) { GPixelRgn pixel_rgn; TileDrawable *drawable; gint32 image_ID; gint32 layer_ID; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; guchar *buf; guchar **rowbuf; char *name; int image_type; int layer_type; int tile_height; int scanlines; int i, start, end; int m; int depth = 8; /* We set up the normal JPEG error routines. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; if ((infile = fopen (filename, "rb")) == NULL) { g_warning ("can't open \"%s\"\n", filename); gimp_quit (); } if( strrchr(filename,'.') && strcmp( strrchr(filename, '.'), ".jp4") == 0 ) depth = 16; name = malloc (strlen (filename) + 12); sprintf (name, "%s %s:", _("Loading"), filename); gimp_progress_init (name); free (name); image_ID = -1; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); if (image_ID != -1) gimp_image_delete (image_ID); gimp_quit (); } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src (&cinfo, infile); setup_read_icc_profile(&cinfo); for (m = 0; m < 16; m++) jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF); /* Step 3: read file parameters with jpeg_read_header() */ (void) jpeg_read_header (&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ prepareColour( &cinfo ); /* Step 5: Start decompressor */ jpeg_start_decompress (&cinfo); /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* temporary buffer */ tile_height = gimp_tile_height (); buf = g_new (guchar, tile_height * cinfo.output_width * cinfo.output_components); rowbuf = g_new (guchar*, tile_height); for (i = 0; i < tile_height; i++) rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i; /* Create a new image of the proper size and associate the filename with it. */ if(depth == 8) { switch (cinfo.output_components) { case 1: image_type = GRAY; layer_type = GRAY_IMAGE; break; case 3: image_type = RGB; layer_type = RGB_IMAGE; break; case 4: image_type = RGB; layer_type = RGBA_IMAGE; break; default: gimp_quit (); } } else { switch (cinfo.output_components) { case 1: image_type = U16_GRAY; layer_type = U16_GRAY_IMAGE; break; case 3: image_type = U16_RGB; layer_type = U16_RGB_IMAGE; break; case 4: image_type = U16_RGB; layer_type = U16_RGBA_IMAGE; break; default: gimp_quit (); } } image_ID = gimp_image_new (cinfo.output_width / (depth/8), cinfo.output_height, image_type); gimp_image_set_filename (image_ID, filename); layer_ID = gimp_layer_new (image_ID, _("Background"), cinfo.output_width / (depth/8), cinfo.output_height, layer_type, 100, NORMAL_MODE); gimp_image_add_layer (image_ID, layer_ID, 0); drawable = gimp_drawable_get (layer_ID); gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, FALSE); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { start = cinfo.output_scanline; end = cinfo.output_scanline + tile_height; end = MIN (end,CAST(int) cinfo.output_height); scanlines = end - start; for (i = 0; i < scanlines; i++) jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1); /* for (i = start; i < end; i++) gimp_pixel_rgn_set_row (&pixel_rgn, tilerow[i - start], 0, i, drawable->width); */ if(cinfo.out_color_space == JCS_CMYK) for(i = 0; i < scanlines*drawable->width*cinfo.output_components; ++i) buf[i] = 255 - buf[i]; if(depth == 16 && 0) for(i = 0; i < scanlines*drawable->width*cinfo.output_components; ++i) { unsigned char c = buf[2*i]; buf[2*i] = buf[2*i+1]; buf[2*i+1] = c; } gimp_pixel_rgn_set_rect (&pixel_rgn, buf, 0, start, drawable->width, scanlines); gimp_progress_update ((double) cinfo.output_scanline / (double) cinfo.output_height); } // Step 6a: read icc profile { LPBYTE Buffer = NULL; size_t Len = 0; cmsHPROFILE hProfile=NULL; if (read_icc_profile(&cinfo, &Buffer, &Len)) { printf ("%s:%d %s() embedded profile found\n",__FILE__,__LINE__,__func__); } else if (read_icc_profile2(&cinfo, &Buffer, &Len)) { printf ("%s:%d %s() default profile selected\n",__FILE__,__LINE__,__func__); } if(Buffer && Len) { hProfile = cmsOpenProfileFromMem(Buffer, Len); if (hProfile) { gimp_image_set_icc_profile_by_mem (image_ID, Len, Buffer, ICC_IMAGE_PROFILE); cmsCloseProfile (hProfile); free(Buffer); printf ("%s:%d %s() set profile\n",__FILE__,__LINE__,__func__); } } } /* Step 7: Finish decompression */ jpeg_finish_decompress (&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress (&cinfo); /* free up the temporary buffers */ g_free (rowbuf); g_free (buf); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose (infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.num_warnings is nonzero). */ /* Tell the GIMP to display the image. */ gimp_drawable_flush (drawable); return image_ID; }