bool CxImageJPG::Encode(CxFile * hFile) { if (EncodeSafeCheck(hFile)) return false; if (head.biClrUsed!=0 && !IsGrayScale()){ strcpy(info.szLastError,"JPEG can save only RGB or GreyScale images"); return false; } /* This struct contains the JPEG compression parameters and pointers to * working space (which is allocated as needed by the JPEG library). * It is possible to have several such structures, representing multiple * compression/decompression processes, in existence at once. We refer * to any one struct (and its associated working data) as a "JPEG object". */ struct jpeg_compress_struct cinfo; /* This struct represents a JPEG error handler. It is declared separately * because applications often want to supply a specialized error handler * (see the second half of this file for an example). But here we just * take the easy way out and use the standard error handler, which will * print a message on stderr and call exit() if compression fails. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ //struct jpeg_error_mgr jerr; /* We use our private extension JPEG error handler. <CSC> */ struct ima_error_mgr jerr; jerr.buffer=info.szLastError; /* More stuff */ int row_stride; /* physical row width in image buffer */ JSAMPARRAY buffer; /* Output row buffer */ /* Step 1: allocate and initialize JPEG compression object */ /* We have to set up the error handler first, in case the initialization * step fails. (Unlikely, but it could happen if you are out of memory.) * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ //cinfo.err = jpeg_std_error(&jerr); <CSC> /* We set up the normal JPEG error routines, then override error_exit. */ cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = ima_jpeg_error_exit; /* 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. */ strcpy(info.szLastError, jerr.buffer); //<CSC> jpeg_destroy_compress(&cinfo); return 0; } /* Now we can initialize the JPEG compression object. */ jpeg_create_compress(&cinfo); /* Step 2: specify data destination (eg, a file) */ /* Note: steps 2 and 3 can be done in either order. */ /* Here we use the library-supplied code to send compressed data to a * stdio stream. You can also write your own code to do something else. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to write binary files. */ //jpeg_stdio_dest(&cinfo, outfile); CxFileJpg dest(hFile); cinfo.dest = &dest; /* Step 3: set parameters for compression */ /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ cinfo.image_width = GetWidth(); // image width and height, in pixels cinfo.image_height = GetHeight(); if (IsGrayScale()){ cinfo.input_components = 1; // # of color components per pixel cinfo.in_color_space = JCS_GRAYSCALE; /* colorspace of input image */ } else { cinfo.input_components = 3; // # of color components per pixel cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ } /* Now use the library's routine to set default compression parameters. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) */ jpeg_set_defaults(&cinfo); /* Now you can set any non-default parameters you wish to. * Here we just illustrate the use of quality (quantization table) scaling: */ //jpeg_set_quality(&cinfo, info.nQuality, TRUE /* limit to baseline-JPEG values */); #ifdef C_ARITH_CODING_SUPPORTED if ((GetCodecOption() & ENCODE_ARITHMETIC) != 0) cinfo.arith_code = TRUE; #endif #ifdef ENTRPY_OPT_SUPPORTED if ((GetCodecOption() & ENCODE_OPTIMIZE) != 0) cinfo.optimize_coding = TRUE; #endif if ((GetCodecOption() & ENCODE_GRAYSCALE) != 0) jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE); if ((GetCodecOption() & ENCODE_SMOOTHING) != 0) cinfo.smoothing_factor = m_nSmoothing; jpeg_set_quality(&cinfo, GetJpegQuality(), (GetCodecOption() & ENCODE_BASELINE) != 0); #ifdef C_PROGRESSIVE_SUPPORTED if ((GetCodecOption() & ENCODE_PROGRESSIVE) != 0) jpeg_simple_progression(&cinfo); #endif #ifdef C_LOSSLES_SUPPORTED if ((GetCodecOption() & ENCODE_LOSSLESS) != 0) jpeg_simple_lossless(&cinfo, m_nPredictor, m_nPointTransform); #endif cinfo.density_unit=1; cinfo.X_density=(unsigned short)GetXDPI(); cinfo.Y_density=(unsigned short)GetYDPI(); /* Step 4: Start compressor */ /* TRUE ensures that we will write a complete interchange-JPEG file. * Pass TRUE unless you are very sure of what you're doing. */ jpeg_start_compress(&cinfo, TRUE); /* Step 5: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* Here we use the library's state variable cinfo.next_scanline as the * loop counter, so that we don't have to keep track ourselves. * To keep things simple, we pass one scanline per call; you can pass * more if you wish, though. */ row_stride = info.dwEffWidth; /* JSAMPLEs per row in image_buffer */ //<DP> "8+row_stride" fix heap deallocation problem during debug??? buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, 8+row_stride, 1); CImageIterator iter(this); iter.Upset(); while (cinfo.next_scanline < cinfo.image_height) { // info.nProgress = (long)(100*cinfo.next_scanline/cinfo.image_height); iter.GetRow(buffer[0], row_stride); // not necessary if swapped red and blue definition in jmorecfg.h;ln322 <W. Morrison> if (head.biClrUsed==0){ // swap R & B for RGB images RGBtoBGR(buffer[0], row_stride); // Lance : 1998/09/01 : Bug ID: EXP-2.1.1-9 } iter.PrevRow(); (void) jpeg_write_scanlines(&cinfo, buffer, 1); } /* Step 6: Finish compression */ jpeg_finish_compress(&cinfo); /* Step 7: release JPEG compression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_compress(&cinfo); /* And we're done! */ return true; }
/** * HistogramStretch * \param method: 0 = luminance (default), 1 = linked channels , 2 = independent channels. * \return true if everything is ok * \author [dave] and [nipper] */ bool CxImage::HistogramStretch(long method) { if (!pDib) return false; if ((head.biBitCount==8) && IsGrayScale()){ // get min/max info BYTE minc = 255, maxc = 0; BYTE gray; long y; double dbScaler = 50.0/head.biHeight; for (y=0; y<head.biHeight; y++) { info.nProgress = (long)(y*dbScaler); for (long x=0; x<head.biWidth; x++) { gray = GetPixelIndex(x, y); if (gray < minc) minc = gray; if (gray > maxc) maxc = gray; } } if (minc == 0 && maxc == 255) return true; // calculate LUT BYTE lut[256]; BYTE range = maxc - minc; if (range != 0){ for (long x = minc; x <= maxc; x++){ lut[x] = (BYTE)(255 * (x - minc) / range); } } else lut[minc] = minc; for (y=0; y<head.biHeight; y++) { info.nProgress = (long)(50.0+y*dbScaler); for (long x=0; x<head.biWidth; x++) { SetPixelIndex(x, y, lut[GetPixelIndex(x, y)]); } } } else { switch(method){ case 1: { // <nipper> // get min/max info BYTE minc = 255, maxc = 0; RGBQUAD color; long y; for (y=0; y<head.biHeight; y++) { for (long x=0; x<head.biWidth; x++) { color = GetPixelColor(x, y); if (color.rgbRed < minc) minc = color.rgbRed; if (color.rgbBlue < minc) minc = color.rgbBlue; if (color.rgbGreen < minc) minc = color.rgbGreen; if (color.rgbRed > maxc) maxc = color.rgbRed; if (color.rgbBlue > maxc) maxc = color.rgbBlue; if (color.rgbGreen > maxc) maxc = color.rgbGreen; } } if (minc == 0 && maxc == 255) return true; // calculate LUT BYTE lut[256]; BYTE range = maxc - minc; if (range != 0){ for (long x = minc; x <= maxc; x++){ lut[x] = (BYTE)(255 * (x - minc) / range); } } else lut[minc] = minc; // normalize image double dbScaler = 100.0/head.biHeight; for (y=0; y<head.biHeight; y++) { info.nProgress = (long)(y*dbScaler); for (long x=0; x<head.biWidth; x++) { color = GetPixelColor(x, y); color.rgbRed = lut[color.rgbRed]; color.rgbBlue = lut[color.rgbBlue]; color.rgbGreen = lut[color.rgbGreen]; SetPixelColor(x, y, color); } } } break; case 2: { // <nipper> // get min/max info BYTE minR = 255, maxR = 0; BYTE minG = 255, maxG = 0; BYTE minB = 255, maxB = 0; RGBQUAD color; long y; for (y=0; y<head.biHeight; y++) { for (long x=0; x<head.biWidth; x++) { color = GetPixelColor(x, y); if (color.rgbRed < minR) minR = color.rgbRed; if (color.rgbBlue < minB) minB = color.rgbBlue; if (color.rgbGreen < minG) minG = color.rgbGreen; if (color.rgbRed > maxR) maxR = color.rgbRed; if (color.rgbBlue > maxB) maxB = color.rgbBlue; if (color.rgbGreen > maxG) maxG = color.rgbGreen; } } if (minR == 0 && maxR == 255 && minG == 0 && maxG == 255 && minB == 0 && maxB == 255) return true; // calculate LUT BYTE lutR[256]; BYTE range = maxR - minR; if (range != 0) { for (long x = minR; x <= maxR; x++){ lutR[x] = (BYTE)(255 * (x - minR) / range); } } else lutR[minR] = minR; BYTE lutG[256]; range = maxG - minG; if (range != 0) { for (long x = minG; x <= maxG; x++){ lutG[x] = (BYTE)(255 * (x - minG) / range); } } else lutG[minG] = minG; BYTE lutB[256]; range = maxB - minB; if (range != 0) { for (long x = minB; x <= maxB; x++){ lutB[x] = (BYTE)(255 * (x - minB) / range); } } else lutB[minB] = minB; // normalize image double dbScaler = 100.0/head.biHeight; for (y=0; y<head.biHeight; y++) { info.nProgress = (long)(y*dbScaler); for (long x=0; x<head.biWidth; x++) { color = GetPixelColor(x, y); color.rgbRed = lutR[color.rgbRed]; color.rgbBlue = lutB[color.rgbBlue]; color.rgbGreen = lutG[color.rgbGreen]; SetPixelColor(x, y, color); } } } break; default: { // <dave> // S = ( R - C ) ( B - A / D - C ) double alimit = 0.0; double blimit = 255.0; double lowerc = 255.0; double upperd = 0.0; double tmpGray; RGBQUAD color; RGBQUAD yuvClr; double stretcheds; if ( head.biClrUsed == 0 ){ long x, y, xmin, xmax, ymin, ymax; xmin = ymin = 0; xmax = head.biWidth; ymax = head.biHeight; for( y = ymin; y < ymax; y++ ){ info.nProgress = (long)(50*y/ymax); for( x = xmin; x < xmax; x++ ){ color = GetPixelColor( x, y ); tmpGray = RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue); if ( tmpGray < lowerc ) lowerc = tmpGray; if ( tmpGray > upperd ) upperd = tmpGray; } } if (upperd==lowerc) return false; for( y = ymin; y < ymax; y++ ){ info.nProgress = (long)(50+50*y/ymax); for( x = xmin; x < xmax; x++ ){ color = GetPixelColor( x, y ); yuvClr = RGBtoYUV(color); // Stretch Luminance tmpGray = (double)yuvClr.rgbRed; stretcheds = (double)(tmpGray - lowerc) * ( (blimit - alimit) / (upperd - lowerc) ); // + alimit; if ( stretcheds < 0.0 ) stretcheds = 0.0; else if ( stretcheds > 255.0 ) stretcheds = 255.0; yuvClr.rgbRed = (BYTE)stretcheds; color = YUVtoRGB(yuvClr); SetPixelColor( x, y, color ); } } } else { DWORD j; for( j = 0; j < head.biClrUsed; j++ ){ color = GetPaletteColor( (BYTE)j ); tmpGray = RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue); if ( tmpGray < lowerc ) lowerc = tmpGray; if ( tmpGray > upperd ) upperd = tmpGray; } if (upperd==lowerc) return false; for( j = 0; j < head.biClrUsed; j++ ){ color = GetPaletteColor( (BYTE)j ); yuvClr = RGBtoYUV( color ); // Stretch Luminance tmpGray = (double)yuvClr.rgbRed; stretcheds = (double)(tmpGray - lowerc) * ( (blimit - alimit) / (upperd - lowerc) ); // + alimit; if ( stretcheds < 0.0 ) stretcheds = 0.0; else if ( stretcheds > 255.0 ) stretcheds = 255.0; yuvClr.rgbRed = (BYTE)stretcheds; color = YUVtoRGB(yuvClr); SetPaletteColor( (BYTE)j, color ); } } } } } return true; }
/** * HistogramStretch * \param method: 0 = luminance (default), 1 = linked channels , 2 = independent channels. * \param threshold: minimum percentage level in the histogram to recognize it as meaningful. Range: 0.0 to 1.0; default = 0; typical = 0.005 (0.5%); * \return true if everything is ok * \author [dave] and [nipper]; changes [DP] */ bool CxImage::HistogramStretch(long method, double threshold) { if (!pDib) return false; double dbScaler = 50.0/head.biHeight; long x,y; if ((head.biBitCount==8) && IsGrayScale()){ double p[256]; memset(p, 0, 256*sizeof(double)); for (y=0; y<head.biHeight; y++) { info.nProgress = (long)(y*dbScaler); if (info.nEscape) break; for (x=0; x<head.biWidth; x++) { p[BlindGetPixelIndex(x, y)]++; } } double maxh = 0; for (y=0; y<255; y++) if (maxh < p[y]) maxh = p[y]; threshold *= maxh; int minc = 0; while (minc<255 && p[minc]<=threshold) minc++; int maxc = 255; while (maxc>0 && p[maxc]<=threshold) maxc--; if (minc == 0 && maxc == 255) return true; if (minc >= maxc) return true; // calculate LUT BYTE lut[256]; for (x = 0; x <256; x++){ lut[x] = (BYTE)max(0,min(255,(255 * (x - minc) / (maxc - minc)))); } for (y=0; y<head.biHeight; y++) { if (info.nEscape) break; info.nProgress = (long)(50.0+y*dbScaler); for (x=0; x<head.biWidth; x++) { BlindSetPixelIndex(x, y, lut[BlindGetPixelIndex(x, y)]); } } } else { switch(method){ case 1: { // <nipper> double p[256]; memset(p, 0, 256*sizeof(double)); for (y=0; y<head.biHeight; y++) { info.nProgress = (long)(y*dbScaler); if (info.nEscape) break; for (x=0; x<head.biWidth; x++) { RGBQUAD color = BlindGetPixelColor(x, y); p[color.rgbRed]++; p[color.rgbBlue]++; p[color.rgbGreen]++; } } double maxh = 0; for (y=0; y<255; y++) if (maxh < p[y]) maxh = p[y]; threshold *= maxh; int minc = 0; while (minc<255 && p[minc]<=threshold) minc++; int maxc = 255; while (maxc>0 && p[maxc]<=threshold) maxc--; if (minc == 0 && maxc == 255) return true; if (minc >= maxc) return true; // calculate LUT BYTE lut[256]; for (x = 0; x <256; x++){ lut[x] = (BYTE)max(0,min(255,(255 * (x - minc) / (maxc - minc)))); } // normalize image for (y=0; y<head.biHeight; y++) { if (info.nEscape) break; info.nProgress = (long)(50.0+y*dbScaler); for (x=0; x<head.biWidth; x++) { RGBQUAD color = BlindGetPixelColor(x, y); color.rgbRed = lut[color.rgbRed]; color.rgbBlue = lut[color.rgbBlue]; color.rgbGreen = lut[color.rgbGreen]; BlindSetPixelColor(x, y, color); } } } break; case 2: { // <nipper> double pR[256]; memset(pR, 0, 256*sizeof(double)); double pG[256]; memset(pG, 0, 256*sizeof(double)); double pB[256]; memset(pB, 0, 256*sizeof(double)); for (y=0; y<head.biHeight; y++) { info.nProgress = (long)(y*dbScaler); if (info.nEscape) break; for (long x=0; x<head.biWidth; x++) { RGBQUAD color = BlindGetPixelColor(x, y); pR[color.rgbRed]++; pB[color.rgbBlue]++; pG[color.rgbGreen]++; } } double maxh = 0; for (y=0; y<255; y++) if (maxh < pR[y]) maxh = pR[y]; double threshold2 = threshold*maxh; int minR = 0; while (minR<255 && pR[minR]<=threshold2) minR++; int maxR = 255; while (maxR>0 && pR[maxR]<=threshold2) maxR--; maxh = 0; for (y=0; y<255; y++) if (maxh < pG[y]) maxh = pG[y]; threshold2 = threshold*maxh; int minG = 0; while (minG<255 && pG[minG]<=threshold2) minG++; int maxG = 255; while (maxG>0 && pG[maxG]<=threshold2) maxG--; maxh = 0; for (y=0; y<255; y++) if (maxh < pB[y]) maxh = pB[y]; threshold2 = threshold*maxh; int minB = 0; while (minB<255 && pB[minB]<=threshold2) minB++; int maxB = 255; while (maxB>0 && pB[maxB]<=threshold2) maxB--; if (minR == 0 && maxR == 255 && minG == 0 && maxG == 255 && minB == 0 && maxB == 255) return true; // calculate LUT BYTE lutR[256]; BYTE range = maxR - minR; if (range != 0) { for (x = 0; x <256; x++){ lutR[x] = (BYTE)max(0,min(255,(255 * (x - minR) / range))); } } else lutR[minR] = minR; BYTE lutG[256]; range = maxG - minG; if (range != 0) { for (x = 0; x <256; x++){ lutG[x] = (BYTE)max(0,min(255,(255 * (x - minG) / range))); } } else lutG[minG] = minG; BYTE lutB[256]; range = maxB - minB; if (range != 0) { for (x = 0; x <256; x++){ lutB[x] = (BYTE)max(0,min(255,(255 * (x - minB) / range))); } } else lutB[minB] = minB; // normalize image for (y=0; y<head.biHeight; y++) { info.nProgress = (long)(50.0+y*dbScaler); if (info.nEscape) break; for (x=0; x<head.biWidth; x++) { RGBQUAD color = BlindGetPixelColor(x, y); color.rgbRed = lutR[color.rgbRed]; color.rgbBlue = lutB[color.rgbBlue]; color.rgbGreen = lutG[color.rgbGreen]; BlindSetPixelColor(x, y, color); } } } break; default: { // <dave> double p[256]; memset(p, 0, 256*sizeof(double)); for (y=0; y<head.biHeight; y++) { info.nProgress = (long)(y*dbScaler); if (info.nEscape) break; for (x=0; x<head.biWidth; x++) { RGBQUAD color = BlindGetPixelColor(x, y); p[RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue)]++; } } double maxh = 0; for (y=0; y<255; y++) if (maxh < p[y]) maxh = p[y]; threshold *= maxh; int minc = 0; while (minc<255 && p[minc]<=threshold) minc++; int maxc = 255; while (maxc>0 && p[maxc]<=threshold) maxc--; if (minc == 0 && maxc == 255) return true; if (minc >= maxc) return true; // calculate LUT BYTE lut[256]; for (x = 0; x <256; x++){ lut[x] = (BYTE)max(0,min(255,(255 * (x - minc) / (maxc - minc)))); } for(y=0; y<head.biHeight; y++){ info.nProgress = (long)(50.0+y*dbScaler); if (info.nEscape) break; for(x=0; x<head.biWidth; x++){ RGBQUAD color = BlindGetPixelColor( x, y ); RGBQUAD yuvClr = RGBtoYUV(color); yuvClr.rgbRed = lut[yuvClr.rgbRed]; color = YUVtoRGB(yuvClr); BlindSetPixelColor( x, y, color ); } } } } } return true; }
bool CxImageJ2K::Encode(CxFile * hFile) { if (EncodeSafeCheck(hFile)) return false; if (head.biClrUsed!=0 && !IsGrayScale()){ strcpy(info.szLastError,"J2K can save only RGB or GrayScale images"); return false; } int i,x,y; j2k_image_t *img; j2k_cp_t *cp; j2k_tcp_t *tcp; j2k_tccp_t *tccp; img = (j2k_image_t *)calloc(sizeof(j2k_image_t),1); cp = (j2k_cp_t *)calloc(sizeof(j2k_cp_t),1); cp->tx0=0; cp->ty0=0; cp->tw=1; cp->th=1; cp->tcps=(j2k_tcp_t*)calloc(sizeof(j2k_tcp_t),1); tcp=&cp->tcps[0]; long w=head.biWidth; long h=head.biHeight; tcp->numlayers=1; for (i=0;i<tcp->numlayers;i++) tcp->rates[i]=(w*h*GetJpegQuality())/600; if (IsGrayScale()) { img->x0=0; img->y0=0; img->x1=w; img->y1=h; img->numcomps=1; img->comps=(j2k_comp_t*)calloc(sizeof(j2k_comp_t),1); img->comps[0].data=(int*)calloc(w*h*sizeof(int),1); img->comps[0].prec=8; img->comps[0].sgnd=0; img->comps[0].dx=1; img->comps[0].dy=1; for (i=0,y=0; y<h; y++) { for (x=0; x<w; x++,i++){ img->comps[0].data[i]=GetPixelIndex(x,h-1-y); } } } else if (!IsIndexed()) { img->x0=0; img->y0=0; img->x1=w; img->y1=h; img->numcomps=3; img->comps=(j2k_comp_t*)calloc(img->numcomps*sizeof(j2k_comp_t),1); for (i=0; i<img->numcomps; i++) { img->comps[i].data=(int*)calloc(w*h*sizeof(int),1); img->comps[i].prec=8; img->comps[i].sgnd=0; img->comps[i].dx=1; img->comps[i].dy=1; } RGBQUAD c; for (i=0,y=0; y<h; y++) { for (x=0; x<w; x++,i++){ c=GetPixelColor(x,h-1-y); img->comps[0].data[i]=c.rgbRed; img->comps[1].data[i]=c.rgbGreen; img->comps[2].data[i]=c.rgbBlue; } } } else { return 0; } cp->tdx=img->x1-img->x0; cp->tdy=img->y1-img->y0; tcp->csty=0; tcp->prg=0; tcp->mct=img->numcomps==3?1:0; tcp->tccps=(j2k_tccp_t*)calloc(img->numcomps*sizeof(j2k_tccp_t),1); int ir=0; /* or 1 ???*/ for (i=0; i<img->numcomps; i++) { tccp=&tcp->tccps[i]; tccp->csty=0; tccp->numresolutions=6; tccp->cblkw=6; tccp->cblkh=6; tccp->cblksty=0; tccp->qmfbid=ir?0:1; tccp->qntsty=ir?J2K_CCP_QNTSTY_SEQNT:J2K_CCP_QNTSTY_NOQNT; tccp->numgbits=2; tccp->roishift=0; j2k_calc_explicit_stepsizes(tccp, img->comps[i].prec); } BYTE* dest=(BYTE*)calloc(tcp->rates[tcp->numlayers-1]+2,1); long len = j2k_encode(img, cp, dest, tcp->rates[tcp->numlayers-1]+2); if (len==0) { strcpy(info.szLastError,"J2K failed to encode image"); } else { hFile->Write(dest, len, 1); } free(dest); j2k_destroy(&img,&cp); return (len!=0); }