00001 /* 00002 File: Image.cc 00003 00004 Function: See header file 00005 00006 Author(s): Andrew Willmott 00007 00008 Copyright: (c) 1997-2000, Andrew Willmott 00009 */ 00010 00011 #include "gcl/Image.h" 00012 #include "gcl/GCLConfig.h" 00013 #include "gcl/VecUtil.h" 00014 00015 #include <iostream.h> 00016 #include <stdio.h> 00017 #include <ctype.h> 00018 00019 00020 // --- Image class ------------------------------------------------------------ 00021 00022 00023 00024 Int Image::sJPEGQuality = 90; 00025 00026 Void Image::Clear(const Colour &c) 00027 // should almost always be overridden 00028 { 00029 Int x, y; 00030 00031 for (y = 0; y < height; y++) 00032 for (x = 0; x < width; x++) 00033 SetPixel(x, y, c); 00034 } 00035 00036 Void Image::CopyFrom(Int x, Int y, Image &img) 00037 { 00038 Int test = img.Width(); 00039 Int copyWidth = Min(img.Width(), width - x); 00040 Int copyHeight = Min(img.Height(), height - y); 00041 Colour *buffer = new Colour[copyWidth]; 00042 Int i; 00043 00044 for (i = 0; i < copyHeight; i++) 00045 { 00046 img.GetSpan(i, x, copyWidth, buffer); 00047 SetSpan(y++, 0, copyWidth, buffer); 00048 } 00049 00050 delete[] buffer; 00051 } 00052 00053 Void Image::GammaCorrect(ClrReal gamma) 00054 { 00055 ClrReal invGamma = 1.0 / gamma; 00056 Colour c; 00057 Int x, y; 00058 00059 for (y = 0; y < Height(); y++) 00060 for (x = 0; x < Width(); x++) 00061 { 00062 c = GetPixel(x, y); 00063 00064 c[0] = pow(c[0], invGamma); 00065 c[1] = pow(c[1], invGamma); 00066 c[2] = pow(c[2], invGamma); 00067 00068 SetPixel(x, y, c); 00069 } 00070 } 00071 00072 Void Image::Scale(ClrReal scale) 00073 { 00074 Colour *buffer = new Colour[width]; 00075 Int i, j; 00076 Colour result; 00077 00078 for (i = 0; i < height; i++) 00079 { 00080 GetSpan(i, 0, width, buffer); 00081 for (j = 0; j < width; j++) 00082 buffer[j] *= scale; 00083 SetSpan(i, 0, width, buffer); 00084 } 00085 } 00086 00087 ClrReal Image::MaxComponent() 00088 { 00089 Int x, y; 00090 ClrReal maxCmpt = 0.0; 00091 Colour c; 00092 00093 for (y = 0; y < height; y++) 00094 for (x = 0; x < width; x++) 00095 { 00096 c = GetPixel(x, y); 00097 maxCmpt = Max(maxCmpt, MaxCmpt(c)); 00098 } 00099 00100 return(maxCmpt); 00101 } 00102 00103 Colour Image::AverageColour() 00104 { 00105 Colour *buffer = new Colour[width]; 00106 Int i, j; 00107 Colour result = cBlack; 00108 00109 for (i = 0; i < height; i++) 00110 { 00111 Colour bsum = cBlack; 00112 00113 GetSpan(i, 0, width, buffer); 00114 for (j = 0; j < width; j++) 00115 bsum += buffer[j]; 00116 bsum /= width; 00117 result += bsum; 00118 } 00119 00120 delete[] buffer; 00121 result /= height; 00122 00123 return(result); 00124 } 00125 00126 Void Image::FindComponentBounds(Colour &min, Colour &max) 00127 { 00128 Int x, y; 00129 Colour c; 00130 00131 for (y = 0; y < height; y++) 00132 for (x = 0; x < width; x++) 00133 { 00134 c = GetPixel(x, y); 00135 FindMinCmpts(min, c, min); 00136 FindMaxCmpts(max, c, max); 00137 } 00138 } 00139 00140 Void Image::Transform(const ClrTrans &trans) 00141 { 00142 Colour *buffer = new Colour[width]; 00143 Int i, j; 00144 00145 for (i = 0; i < height; i++) 00146 { 00147 GetSpan(i, 0, width, buffer); 00148 for (j = 0; j < width; j++) 00149 { 00150 buffer[j] = xform(trans, buffer[j]); 00151 ClipColour(buffer[j]); 00152 } 00153 SetSpan(i, 0, width, buffer); 00154 } 00155 00156 delete[] buffer; 00157 } 00158 00159 Void Image::Over(Int x, Int y, Image &img) 00160 { 00161 Int test = img.Width(); 00162 Int copyWidth = Min(img.Width(), width - x); 00163 Int copyHeight = Min(img.Height(), height - y); 00164 Colour *buffer = new Colour[copyWidth]; 00165 Int i; 00166 00167 // XXX UNFINISHED: want to do composite operation: d = d * (1 - s.alpha) + s 00168 00169 for (i = 0; i < copyHeight; i++) 00170 { 00171 img.GetSpan(i, x, copyWidth, buffer); 00172 SetSpan(y++, 0, copyWidth, buffer); 00173 } 00174 00175 delete[] buffer; 00176 } 00177 00178 Void Image::DownSample(Image &out) 00180 { 00181 Int i, j; 00182 static ColourList span1, span2, outSpan; 00183 00184 out.SetSize(Width() / 2, Height() / 2); 00185 span1.SetSize(Width()); 00186 span2.SetSize(Width()); 00187 outSpan.SetSize(out.Width()); 00188 00189 for (i = 0; i < out.Height(); i++) 00190 { 00191 GetSpan(2 * i, 0, Width(), span1.Ref()); 00192 GetSpan(2 * i + 1, 0, Width(), span2.Ref()); 00193 00194 for (j = 0; j < out.Width(); j++) 00195 { 00196 outSpan[j] = span1[2 * j] + span1[2 * j + 1]; 00197 outSpan[j] += span2[2 * j] + span2[2 * j + 1]; 00198 outSpan[j] /= 4.0; 00199 } 00200 00201 out.SetSpan(i, 0, out.Width(), outSpan.Ref()); 00202 } 00203 } 00204 00205 00206 // These should almost always be overridden with more efficient routines 00207 00208 Void Image::SetRealPixel(Int x, Int y, ClrReal r, ImgChannel channel) 00209 { 00210 if (channel == chMono) 00211 SetPixel(x, y, cWhite * r); 00212 else if (channel <= chBlue) 00213 { 00214 Colour c = GetPixel(x, y); 00215 00216 c[channel - chRed] = r; 00217 SetPixel(x, y, c); 00218 } 00219 } 00220 00221 ClrReal Image::GetRealPixel(Int x, Int y, ImgChannel channel) const 00222 { 00223 if (channel <= chBlue) 00224 { 00225 Colour c = GetPixel(x, y); 00226 00227 if (channel == chMono) 00228 return(dot(cWhite, c) * (1.0 / 3.0)); 00229 else 00230 return(c[channel - chRed]); 00231 } 00232 else 00233 return(0.0); 00234 } 00235 00236 Void Image::SetRealSpan(Int row, Int start, Int length, 00237 const ClrReal *src, ImgChannel channel) 00238 { 00239 Int i; 00240 00241 for (i = 0; i < length; i++) 00242 SetRealPixel(start + i, row, src[i], channel); 00243 } 00244 00245 Void Image::GetRealSpan(Int row, Int start, Int length, 00246 ClrReal *dst, ImgChannel channel) const 00247 { 00248 Int i; 00249 00250 for (i = 0; i < length; i++) 00251 dst[i] = GetRealPixel(start + i, row, channel); 00252 } 00253 00254 Void Image::SetRGBASpan(Int row, Int start, Int length, const RGBAPixel *src) 00255 { 00256 Int i; 00257 Colour *buffer = new Colour[length]; 00258 00259 for (i = 0; i < length; i++) 00260 buffer[i] = RGBAToColour(src[i]); 00261 00262 SetSpan(row, start, length, buffer); 00263 00264 delete[] buffer; 00265 } 00266 00267 Void Image::GetRGBASpan(Int row, Int start, Int length, RGBAPixel *dst) const 00268 { 00269 Int i; 00270 Colour *buffer = new Colour[length]; 00271 00272 GetSpan(row, start, length, buffer); 00273 00274 for (i = 0; i < length; i++) 00275 dst[i] = ColourToRGBA(buffer[i]); 00276 00277 delete[] buffer; 00278 } 00279 00280 Void Image::SetByteSpan(Int row, Int start, Int length, 00281 const Byte *src, ImgChannel channel) 00282 { 00283 Int i; 00284 Colour *buffer = new Colour[length]; 00285 00286 for (i = 0; i < length; i++) 00287 buffer[i] = ByteToColour(src[i]); 00288 00289 SetSpan(row, start, length, buffer); 00290 00291 delete[] buffer; 00292 } 00293 00294 Void Image::GetByteSpan(Int row, Int start, Int length, 00295 Byte *dst, ImgChannel channel) const 00296 { 00297 Int i; 00298 Colour *buffer = new Colour[length]; 00299 00300 GetSpan(row, start, length, buffer); 00301 00302 for (i = 0; i < length; i++) 00303 dst[i] = ColourToByte(buffer[i]); 00304 00305 delete[] buffer; 00306 } 00307 00308 00309 // --- File methods ----------------------------------------------------------- 00310 00311 Char *tImageFormats[] = 00312 { 00313 "ppm", "PPM image format", 00314 #ifdef GCL_TIFF 00315 "tif", "Adobe TIFF format", 00316 #endif 00317 #ifdef GCL_JPEG 00318 "jpg", "JPEG format", 00319 #endif 00320 #ifdef GCL_GIF 00321 "gif", "GIF format (read only)", 00322 #endif 00323 0 00324 }; 00325 00326 Void Image::PrintSupportedFormats(ostream &s) 00327 { 00328 Char **p = tImageFormats; 00329 00330 s << "Supported image file extensions: " << endl; 00331 00332 while (*p) 00333 { 00334 s << String().Printf("%-5s %s\n", *p, *(p + 1)); 00335 p += 2; 00336 } 00337 } 00338 00339 Int Image::Save(FileName &filename) 00340 { 00341 if (filename.GetExtension() == "ppm") 00342 return(SavePPM(filename.GetPath())); 00343 #ifdef GCL_TIFF 00344 else if (filename.GetExtension() == "tiff" || filename.GetExtension() == "tif") 00345 return(SaveTIFF(filename.GetPath())); 00346 #endif 00347 #ifdef GCL_JPEG 00348 else if (filename.GetExtension() == "jpg") 00349 return(SaveJPEG(filename.GetPath())); 00350 #endif 00351 else 00352 { 00353 #ifdef GCL_TIFF 00354 if (filename.GetExtension() != "") 00355 cerr << "Unknown image file extension:" 00356 << " saving in TIFF format" << endl; 00357 filename.SetExtension("tif"); 00358 return(SaveTIFF(filename.GetPath())); 00359 #else 00360 if (filename.GetExtension() != "") 00361 cerr << "Unknown image file extension:" 00362 << " saving in PPM format" << endl; 00363 filename.SetExtension("ppm"); 00364 return(SavePPM(filename.GetPath())); 00365 #endif 00366 } 00367 } 00368 00369 static StrConst kImageExtensions[] = 00370 { 00371 "ppm", 00372 "pgm", 00373 #ifdef GCL_TIFF 00374 "tif", 00375 "tiff", 00376 #endif 00377 #ifdef GCL_JPEG 00378 "jpg", 00379 #endif 00380 #ifdef GCL_GIF 00381 "gif", 00382 #endif 00383 0 00384 }; 00385 00386 Int Image::Load(FileName &filename) 00387 { 00388 Int result = filename.FindFileExtension(kImageExtensions); 00389 00390 if (result == kBadExtension) 00391 cerr << "error: unknown image extension '" << filename.GetExtension() 00392 << "'" << endl; 00393 else if (result == kFileNotFound) 00394 cerr << "error: can't find image file " << filename.GetPath() << endl; 00395 00396 if (result < 0) 00397 return(result); 00398 00399 filename.DecompressSetup(); 00400 if (result < 2) 00401 return(LoadPPM(filename.GetPath())); 00402 #ifdef GCL_TIFF 00403 else if (filename.GetExtension() == "tiff" || filename.GetExtension() == "tif") 00404 return(LoadTIFF(filename.GetPath())); 00405 #endif 00406 #ifdef GCL_JPEG 00407 else if (filename.GetExtension() == "jpg") 00408 return(LoadJPEG(filename.GetPath())); 00409 #endif 00410 #ifdef GCL_GIF 00411 else if (filename.GetExtension() == "gif") 00412 return(LoadGIF(filename.GetPath())); 00413 #endif 00414 filename.DecompressCleanup(); 00415 00416 _Error("Unhandled extension"); 00417 00418 return(-1); 00419 } 00420 00421 00422 // --- PPM read/write --------------------------------------------------------- 00423 00424 00425 Int Image::SavePPM(const Char *filename) 00426 { 00427 int i, j; 00428 FILE *ppm; 00429 RGBAPixel *p, *buffer = new RGBAPixel[width]; 00430 00431 if (!buffer) 00432 return(-1); 00433 ppm = fopen(filename, "w"); 00434 if (!ppm) 00435 return(-1); 00436 00437 fprintf(ppm, "P6\n%u %u\n255\n", width, height); 00438 00439 for (j = height - 1; j >= 0; j--) 00440 { 00441 GetRGBASpan(j, 0, width, buffer); 00442 p = buffer; 00443 for (i = 0; i < width; i++, p++) 00444 { 00445 fputc(p->ch[rgba_R], ppm); 00446 fputc(p->ch[rgba_G], ppm); 00447 fputc(p->ch[rgba_B], ppm); 00448 } 00449 } 00450 00451 delete[] buffer; 00452 return(fclose(ppm)); 00453 } 00454 00455 static Int ppmReadInt(FILE *ppm) 00456 { 00457 Int result, c; 00458 00459 // chomp space & comments 00460 c = fgetc(ppm); 00461 00462 while(isspace(c) || c == '#') 00463 { 00464 if (c == '#') 00465 do 00466 c = fgetc(ppm); 00467 while (c != '\n' && c != '\r' && c != EOF); 00468 00469 c = fgetc(ppm); 00470 } 00471 00472 ungetc(c, ppm); 00473 fscanf(ppm, "%d", &result); 00474 00475 return(result); 00476 } 00477 00478 Int Image::LoadPPM(const Char *filename) 00479 { 00480 Int i, j; 00481 Int x, y, level; 00482 Char p1, p2; 00483 Bool isMono; 00484 FILE *ppm; 00485 RGBAPixel *p, *buffer; 00486 Byte *mp, *mBuffer; 00487 00488 ppm = fopen(filename, "r"); 00489 if (!ppm) 00490 return(-1); 00491 00492 p1 = fgetc(ppm); 00493 p2 = fgetc(ppm); 00494 00495 if (p1 != 'P' || p2 <= '1' || p2 > '6') 00496 { 00497 _Warning("(LoadPPM) not a PPM file"); 00498 return(-1); 00499 } 00500 00501 if (p2 < '2' || p2 == '4' || p2 > '6') 00502 { 00503 _Warning("(LoadPPM) Only handles PGM and PPM files"); 00504 return(-1); 00505 } 00506 00507 isMono = (p2 == '2' || p2 == '5'); 00508 00509 x = ppmReadInt(ppm); 00510 y = ppmReadInt(ppm); 00511 level = ppmReadInt(ppm); 00512 00513 if (level != 255) 00514 _Warning("(LoadPPM) Ignoring scaling factor."); 00515 00516 SetSize(x, y); 00517 00518 if (fgetc(ppm) != '\n') 00519 { 00520 _Warning("(LoadPPM) Corrupt PPM file"); 00521 return(-1); 00522 } 00523 00524 if (isMono) 00525 mBuffer = new Byte[width]; 00526 else 00527 buffer = new RGBAPixel[width]; 00528 00529 for (j = height - 1; j >= 0; j--) 00530 { 00531 if (p2 == '2') 00532 for (i = 0, mp = mBuffer; i < width; i++, mp++) 00533 { 00534 Int b; 00535 00536 fscanf(ppm, "%d", &b); 00537 *mp = b; 00538 } 00539 else if (p2 == '3') 00540 for (i = 0, p = buffer; i < width; i++, p++) 00541 { 00542 Int b; 00543 fscanf(ppm, "%d", &b); 00544 p->ch[rgba_R] = b; 00545 fscanf(ppm, "%d", &b); 00546 p->ch[rgba_G] = b; 00547 fscanf(ppm, "%d", &b); 00548 p->ch[rgba_B] = b; 00549 p->ch[rgba_A] = 255; 00550 } 00551 else if (p2 == '5') 00552 fread(mBuffer, 1, width, ppm); 00553 else if (p2 == '6') 00554 for (i = 0, p = buffer; i < width; i++, p++) 00555 { 00556 // UUU #ifdef endianess here sometime 00557 p->ch[rgba_R] = fgetc(ppm); 00558 p->ch[rgba_G] = fgetc(ppm); 00559 p->ch[rgba_B] = fgetc(ppm); 00560 p->ch[rgba_A] = 255; 00561 } 00562 00563 if (isMono) 00564 SetByteSpan(j, 0, width, mBuffer); 00565 else 00566 SetRGBASpan(j, 0, width, buffer); 00567 } 00568 00569 if (isMono) 00570 delete[] mBuffer; 00571 else 00572 delete[] buffer; 00573 00574 return(fclose(ppm)); 00575 } 00576 00577 00578 // --- TIFF read/write -------------------------------------------------------- 00579 00580 00581 #ifdef GCL_TIFF 00582 00583 #include "tiffio.h" 00584 00585 Int Image::SaveTIFF(const Char *filename) 00586 { 00587 TIFF *tif; 00588 UInt32 samples_per_pixel = 3; 00589 UInt32 w = Width(); 00590 UInt32 h = Height(); 00591 UInt32 scanline_size = samples_per_pixel * w; 00592 UInt32 i, k; 00593 Int y; 00594 Char *scanline_buf; 00595 RGBAPixel *rgba_buf; 00596 00597 tif = TIFFOpen(filename, "w"); 00598 if (!tif) 00599 return(-1); 00600 00601 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w); 00602 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h); 00603 00604 /* These are the charateristics of our Pic data */ 00605 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_LEFTTOP); 00606 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel); 00607 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); 00608 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); 00609 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); 00610 #ifdef GCL_PATENTS_SUCK 00611 // use PKZIP compression to escape the pitiful unisys nonsense. 00612 // currently, RH 6.2's tiff libs print a pissy little message 00613 // LZW and don't use it, but they haven't enabled support 00614 // for deflate compression. I suppose that would have just 00615 // been too bright of them. If you want compressed images, 00616 // you'll have to rebuild with either LZW reenabled, or deflate 00617 // enabled. 00618 TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE); 00619 #else 00620 TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW); 00621 #endif 00622 00623 if( TIFFScanlineSize(tif) != scanline_size ) 00624 { 00625 fprintf(stderr, 00626 "TIFF: Mismatch with library's expected scanline size!\n"); 00627 return(-1); 00628 } 00629 00630 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); 00631 00632 scanline_buf = new Char[scanline_size]; 00633 rgba_buf = new RGBAPixel[scanline_size]; 00634 00635 if (!scanline_buf) 00636 { 00637 fprintf(stderr, "TIFF: Can't allocate scanline buffer!\n"); 00638 return(-1); 00639 } 00640 00641 for (y = h - 1; y >= 0; y--) 00642 { 00643 GetRGBASpan(y, 0, w, rgba_buf); 00644 k = 0; 00645 for (i = 0; i < w; i++) 00646 { 00647 scanline_buf[k++] = rgba_buf[i].ch[rgba_R]; 00648 scanline_buf[k++] = rgba_buf[i].ch[rgba_G]; 00649 scanline_buf[k++] = rgba_buf[i].ch[rgba_B]; 00650 } 00651 00652 TIFFWriteScanline(tif, scanline_buf, h - y - 1, 0); 00653 /* note that TIFFWriteScanline modifies the buffer you pass it */ 00654 } 00655 00656 delete[] scanline_buf; 00657 delete[] rgba_buf; 00658 TIFFClose(tif); 00659 00660 return(0); 00661 } 00662 00663 Int Image::LoadTIFF(const Char *filename) 00664 { 00665 TIFF *tif; 00666 TIFFRGBAImage img; 00667 Int result = -1; 00668 Char emsg[1024]; 00669 00670 tif = TIFFOpen(filename, "r"); 00671 if (!tif) 00672 { 00673 _Warning("(LoadTIFF) Couldn't find file."); 00674 return(-1); 00675 } 00676 00677 if (TIFFRGBAImageBegin(&img, tif, 0, emsg)) 00678 { 00679 SetSize(img.width, img.height); 00680 00681 #ifdef GCL_BIG_END 00682 // TIFF unpacks data in AGBR format, which is endian dependent. 00683 // If we're on big-end machines, it matches with our format, 00684 // and we can write the data straight in, otherwise we have to 00685 // convert. 00686 if (Tag() == imgRGBATag) 00687 TIFFRGBAImageGet(&img, ((RGBAImage*) this)->Data(), 00688 img.width, img.height); 00689 else 00690 { 00691 #endif 00692 Int npixels = img.width * img.height; 00693 UInt32 *raster = 00694 (UInt32*) _TIFFmalloc(npixels * sizeof (UInt32)); 00695 00696 // should use TIFF put methods here, but they're too poorly 00697 // documented to waste time on just at the mo' 00698 if (raster) 00699 { 00700 if (TIFFRGBAImageGet(&img, (uint32*) raster, img.width, img.height)) 00701 { 00702 Int i, j; 00703 RGBAPixel *rgbaBuf = new RGBAPixel[img.width]; 00704 00705 if (rgbaBuf) 00706 for (i = 0; i < img.height; i++) 00707 { 00708 for (j = 0; j < img.width; j++) 00709 { 00710 UInt32 pixel = raster[i * img.width + j]; 00711 00712 rgbaBuf[j].ch[rgba_R] = TIFFGetR(pixel); 00713 rgbaBuf[j].ch[rgba_G] = TIFFGetG(pixel); 00714 rgbaBuf[j].ch[rgba_B] = TIFFGetB(pixel); 00715 rgbaBuf[j].ch[rgba_A] = TIFFGetA(pixel); 00716 } 00717 SetRGBASpan(i, 0, img.width, rgbaBuf); 00718 } 00719 00720 delete[] rgbaBuf; 00721 } 00722 else 00723 { 00724 _Warning("(LoadTIFF) TIFFRGBAImageGet failed"); 00725 return(-1); 00726 } 00727 _TIFFfree(raster); 00728 } 00729 else 00730 { 00731 _Warning("(LoadTIFF) couldn't allocate raster data"); 00732 return(-1); 00733 } 00734 00735 #ifdef GCL_BIG_END 00736 } 00737 #endif 00738 00739 TIFFRGBAImageEnd(&img); 00740 result = 0; 00741 } 00742 else 00743 TIFFError("(LoadTIFF)", emsg); 00744 00745 00746 TIFFClose(tif); 00747 00748 return(result); 00749 } 00750 00751 #ifdef UNFINISHED 00752 00753 // code for ward's clever scaling scheme 00754 00755 To a standard TIFF writer, one needs to add the following code fragment 00756 to the initialization routine to set up for sending XYZ data: 00757 00758 Int Image::SaveTIFF_LogLUV(const Char *filename, Float stonits) 00759 { 00760 TIFF *tif; 00761 UInt32 samples_per_pixel = 3; 00762 UInt32 w = Width(); 00763 UInt32 h = Height(); 00764 UInt32 scanline_size = samples_per_pixel * w; 00765 UInt32 y, i, k; 00766 Char *scanline_buf; 00767 RGBAPixel *rgba_buf; 00768 00769 tif = TIFFOpen(filename, "w"); 00770 if (!tif) 00771 return(-1); 00772 00773 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w); 00774 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h); 00775 00776 /* These are the charateristics of our Pic data */ 00777 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_LEFTTOP); 00778 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel); 00779 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); 00780 // TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); 00781 // TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); 00782 // TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW); 00783 // TIFFSetField(tif, TIFFTAG_PREDICTOR, 2); 00784 00785 // set up for LUV output... 00786 TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_SGILOG); 00787 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_LOGLUV); 00788 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); 00789 TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT); 00790 TIFFSetField(tif, TIFFTAG_STONITS, stonits); 00791 00792 if( TIFFScanlineSize(tif) != scanline_size ) 00793 { 00794 _Warning("TIFF: Mismatch with library's expected scanline size!\n"); 00795 return(-1); 00796 } 00797 00798 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); 00799 00800 scanline_buf = new Char[scanline_size]; 00801 rgba_buf = new RGBAPixel[scanline_size]; 00802 00803 if (!scanline_buf) 00804 { 00805 Warning("TIFF: Can't allocate scanline buffer!\n"); 00806 return(-1); 00807 } 00808 00809 for(y = h - 1; y >= 0; y--) 00810 { 00811 GetRGBASpan(y, 0, w, rgba_buf); 00812 k = 0; 00813 for (i = 0; i < w; i++) 00814 { 00815 scanline_buf[k++] = rgba_buf[i].ch[rgba_R]; 00816 scanline_buf[k++] = rgba_buf[i].ch[rgba_G]; 00817 scanline_buf[k++] = rgba_buf[i].ch[rgba_B]; 00818 } 00819 00820 TIFFWriteScanline(tif, scanline_buf, y, 0); 00821 /* note that TIFFWriteScanline modifies the buffer you pass it */ 00822 } 00823 delete[] scanline_buf; 00824 delete[] rgba_buf; 00825 TIFFClose(tif); 00826 return(0); 00827 } 00828 00829 #endif 00830 #endif 00831 00832 00833 // --- GIF read/write -------------------------------------------------------- 00834 00835 #ifdef GCL_GIF 00836 00837 // Bog off, UNISYS 00838 00839 extern "C" 00840 { 00841 #include "gif_lib.h" 00842 } 00843 00844 // why on earth isn't this handled by DGifSlurp? 00845 // and why does that code assume char is unsigned? 00846 static Int tInterlacedOffset[] = { 0, 4, 2, 1 }; 00847 static Int tInterlacedJump[] = { 8, 8, 4, 2 }; 00848 00849 Int Image::SaveGIF(const Char *filename) 00850 { 00851 return(-1); 00852 } 00853 00854 Int Image::LoadGIF(const Char *filename) 00855 { 00856 GifFileType *gifFile; 00857 SavedImage *theGIFImg; 00858 ColorMapObject *clrMap; 00859 RGBAPixel *buffer; 00860 Int i, j, result = -1; 00861 Int pass, destRow, destStep; 00862 00863 gifFile = DGifOpenFileName(filename); 00864 if (gifFile) 00865 { 00866 if (DGifSlurp(gifFile) == GIF_OK) 00867 { 00868 buffer = new RGBAPixel[gifFile->SWidth]; 00869 00870 theGIFImg = gifFile->SavedImages; // first image 00871 00872 SetSize(theGIFImg->ImageDesc.Width, theGIFImg->ImageDesc.Height); 00873 00874 if (theGIFImg->ImageDesc.ColorMap) 00875 clrMap = theGIFImg->ImageDesc.ColorMap; 00876 else if (gifFile->SColorMap) 00877 clrMap = gifFile->SColorMap; 00878 else 00879 { 00880 _Warning("GIF file contains no colormap!"); 00881 return(-1); 00882 } 00883 00884 if (theGIFImg->ImageDesc.Interlace) 00885 { 00886 destRow = tInterlacedOffset[0]; 00887 destStep = tInterlacedJump[0]; 00888 pass = 0; 00889 } 00890 else 00891 { 00892 destRow = 0; 00893 destStep = 1; 00894 } 00895 00896 for (i = 0; i < theGIFImg->ImageDesc.Height; i++) 00897 { 00898 Byte *sp = (Byte*) theGIFImg->RasterBits + i * theGIFImg->ImageDesc.Width; 00899 00900 for (j = 0; j < theGIFImg->ImageDesc.Width; j++) 00901 { 00902 Byte idx = *sp++; 00903 00904 buffer[j].ch[rgba_R] = clrMap->Colors[idx].Red; 00905 buffer[j].ch[rgba_G] = clrMap->Colors[idx].Green; 00906 buffer[j].ch[rgba_B] = clrMap->Colors[idx].Blue; 00907 buffer[j].ch[rgba_A] = 255; 00908 } 00909 00910 SetRGBASpan(height - 1 - destRow, 0, theGIFImg->ImageDesc.Width, buffer); 00911 destRow += destStep; 00912 if (destRow >= height && theGIFImg->ImageDesc.Interlace) 00913 { 00914 pass++; 00915 Assert(pass < 4, "Too many passes in GIF file"); 00916 destRow = tInterlacedOffset[pass]; 00917 destStep = tInterlacedJump[pass]; 00918 } 00919 } 00920 00921 delete[] buffer; 00922 result = 0; 00923 } 00924 else 00925 PrintGifError(); 00926 00927 if (DGifCloseFile(gifFile) != GIF_OK) 00928 PrintGifError(); 00929 } 00930 else 00931 PrintGifError(); 00932 00933 return(result); 00934 } 00935 00936 #endif 00937 00938 00939 // --- JPEG read/write -------------------------------------------------------- 00940 00941 #ifdef GCL_JPEG 00942 00943 extern "C" 00944 { 00945 #include "jpeglib.h" 00946 } 00947 00948 Int Image::SaveJPEG(const Char *filename) 00949 { 00950 FILE *outfile; 00951 JSAMPROW rowPtr[1]; 00952 struct jpeg_compress_struct compressObj; 00953 struct jpeg_error_mgr jerr; 00954 JSAMPLE *scanlineBuf, *p; 00955 Int i, j; 00956 00957 compressObj.err = jpeg_std_error(&jerr); 00958 jpeg_create_compress(&compressObj); 00959 00960 // Set up file for output... 00961 00962 if ((outfile = fopen(filename, "wb")) == NULL) 00963 { 00964 _Warning("(Image::SaveJPEG) can't open for writing: " + StrConst(filename)); 00965 return(-1); 00966 } 00967 00968 jpeg_stdio_dest(&compressObj, outfile); 00969 00970 compressObj.image_width = width; 00971 compressObj.image_height = height; 00972 compressObj.input_components = 3; 00973 compressObj.in_color_space = JCS_RGB; 00974 00975 jpeg_set_defaults(&compressObj); 00976 jpeg_set_quality(&compressObj, sJPEGQuality, TRUE); 00977 00978 // Set up scanlines 00979 00980 scanlineBuf = new JSAMPLE[width * 3]; 00981 if (!scanlineBuf) 00982 return(-1); 00983 rowPtr[0] = scanlineBuf; 00984 00985 // Compress & save 00986 00987 jpeg_start_compress(&compressObj, TRUE); 00988 00989 for (i = height - 1; i >= 0; i--) 00990 { 00991 Colour c; 00992 00993 p = scanlineBuf; 00994 for (j = 0; j < width; j++) 00995 { 00996 c = GetPixel(j, i); 00997 *p++ = Byte(c[0] * 255.0); 00998 *p++ = Byte(c[1] * 255.0); 00999 *p++ = Byte(c[2] * 255.0); 01000 } 01001 01002 jpeg_write_scanlines(&compressObj, rowPtr, 1); 01003 } 01004 01005 jpeg_finish_compress(&compressObj); 01006 fclose(outfile); 01007 delete[] scanlineBuf; 01008 } 01009 01010 Int Image::LoadJPEG(const Char *filename) 01011 { 01012 struct jpeg_decompress_struct cinfo; 01013 struct jpeg_error_mgr jerr; 01014 JSAMPROW buffer; 01015 FILE *file; 01016 RGBAPixel *rgba_buf; 01017 Int i, j, k, err = -1; 01018 01019 cinfo.err = jpeg_std_error(&jerr); 01020 jpeg_create_decompress(&cinfo); 01021 01022 file = fopen(filename, "rb"); 01023 if (!file) 01024 { 01025 fprintf(stderr, "JPEG: could not open file \"%s\"\n", filename); 01026 return(err); 01027 } 01028 01029 jpeg_stdio_src(&cinfo, file); 01030 jpeg_read_header(&cinfo, TRUE); 01031 01032 // decompress options 01033 cinfo.out_color_space = JCS_RGB; 01034 cinfo.quantize_colors = FALSE; 01035 cinfo.do_fancy_upsampling = FALSE; 01036 cinfo.do_block_smoothing = FALSE; 01037 01038 jpeg_start_decompress(&cinfo); 01039 01040 SetSize(cinfo.output_width, cinfo.output_height); 01041 01042 // JSAMPLE better be a byte. 01043 Assert(sizeof(JSAMPLE) == 1, "JSAMPLE is not a byte"); 01044 Assert(cinfo.output_components == 3, "JCS_RGB request failed"); 01045 buffer = (JSAMPROW) new Byte[cinfo.output_width * cinfo.output_components]; 01046 rgba_buf = new RGBAPixel[cinfo.output_width]; 01047 01048 if (!buffer || !rgba_buf) 01049 { 01050 delete[] buffer; delete[] rgba_buf; 01051 fprintf(stderr, "JPEG: out of memory\n"); 01052 goto bye; 01053 } 01054 01055 k = cinfo.output_height; 01056 01057 while (cinfo.output_scanline < cinfo.output_height) 01058 { 01059 jpeg_read_scanlines(&cinfo, &buffer, 1); 01060 01061 for (i = 0, j = 0; i < cinfo.output_width; i++) 01062 { 01063 rgba_buf[i].ch[rgba_R] = buffer[j++]; 01064 rgba_buf[i].ch[rgba_G] = buffer[j++]; 01065 rgba_buf[i].ch[rgba_B] = buffer[j++]; 01066 rgba_buf[i].ch[rgba_A] = 255; 01067 } 01068 SetRGBASpan(--k, 0, cinfo.output_width, rgba_buf); 01069 } 01070 01071 jpeg_finish_decompress(&cinfo); 01072 err = 0; 01073 delete[] buffer; 01074 delete[] rgba_buf; 01075 01076 bye: 01077 jpeg_destroy_decompress(&cinfo); 01078 01079 fclose(file); 01080 return(err); 01081 } 01082 01083 #endif 01084 01085 01086 // --- RGBAImage methods ------------------------------------------------------ 01087 01088 Void RGBAImage::SetSize(Int width, Int height) 01089 { 01090 delete[] data; 01091 data = new RGBAPixel[width * height]; 01092 SELF.width = width; 01093 SELF.height = height; 01094 } 01095 01096 Void RGBAImage::Clear(const Colour &c) 01097 { 01098 RGBAPixel *p = data, bc = ColourToRGBA(c); 01099 Int i; 01100 01101 for (i = 0; i < width * height; i++, p++) 01102 *p = bc; 01103 } 01104 01105 Void RGBAImage::SetPixel(Int x, Int y, const Colour &c) 01106 { 01107 Assert(x >= 0 && y >= 0 && x < width && y < height, 01108 "illegal pixel access."); 01109 data[x + y * width] = ColourToRGBA(c); 01110 } 01111 01112 Colour RGBAImage::GetPixel(Int x, Int y) const 01113 { 01114 Assert(x >= 0 && y >= 0 && x < width && y < height, 01115 "illegal pixel access."); 01116 return(RGBAToColour(data[x + y * width])); 01117 } 01118 01119 Void RGBAImage::SetSpan(Int row, Int start, Int length, const Colour *src) 01120 { 01121 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01122 "(RGBAImage::SetSpan) illegal span access."); 01123 Int i; 01124 RGBAPixel *p = data + row * width + start; 01125 const Colour *cp = src; 01126 01127 for (i = 0; i < length; i++, p++, cp++) 01128 *p = ColourToRGBA(*cp); 01129 } 01130 01131 Void RGBAImage::GetSpan(Int row, Int start, Int length, Colour *dst) const 01132 { 01133 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01134 "(RGBAImage::GetSpan) illegal span access."); 01135 Int i; 01136 RGBAPixel *p = data + row * width + start; 01137 Colour *cp = dst; 01138 01139 for (i = 0; i < length; i++, p++, cp++) 01140 *cp = RGBAToColour(*p); 01141 } 01142 01143 Void RGBAImage::SetRGBASpan(Int row, Int start, Int length, 01144 const RGBAPixel *src) 01145 { 01146 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01147 "(RGBAImage::SetRGBASpan) illegal span access."); 01148 01149 memcpy(data + row * width + start, src, length * sizeof(RGBAPixel)); 01150 } 01151 01152 Void RGBAImage::GetRGBASpan(Int row, Int start, Int length, 01153 RGBAPixel *dst) const 01154 { 01155 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01156 "(RGBAImage::GetRGBASpan) illegal span access."); 01157 01158 memcpy(dst, data + row * width + start, length * sizeof(RGBAPixel)); 01159 } 01160 01161 01162 // --- ByteImage methods ------------------------------------------------------ 01163 01164 Void ByteImage::SetSize(Int width, Int height) 01165 { 01166 delete[] data; 01167 data = new Byte[width * height]; 01168 SELF.width = width; 01169 SELF.height = height; 01170 } 01171 01172 Void ByteImage::Clear(const Colour &c) 01173 { 01174 Byte *p = data, bc = ColourToByte(c); 01175 Int i; 01176 01177 for (i = 0; i < width * height; i++, p++) 01178 *p = bc; 01179 } 01180 01181 Void ByteImage::SetPixel(Int x, Int y, const Colour &c) 01182 { 01183 Assert(x >= 0 && y >= 0 && x < width && y < height, 01184 "illegal pixel access."); 01185 data[x + y * width] = ColourToByte(c); 01186 } 01187 01188 Colour ByteImage::GetPixel(Int x, Int y) const 01189 { 01190 Assert(x >= 0 && y >= 0 && x < width && y < height, 01191 "illegal pixel access."); 01192 return(ByteToColour(data[x + y * width])); 01193 } 01194 01195 Void ByteImage::SetRealPixel(Int x, Int y, ClrReal r, ImgChannel ch) 01196 { 01197 Assert(x >= 0 && y >= 0 && x < width && y < height, 01198 "illegal pixel access."); 01199 if (ch <= chBlue) 01200 data[x + y * width] = (Byte) (r * 255.0); 01201 } 01202 01203 01204 ClrReal ByteImage::GetRealPixel(Int x, Int y, ImgChannel ch) const 01205 { 01206 Assert(x >= 0 && y >= 0 && x < width && y < height, 01207 "illegal pixel access."); 01208 01209 if (ch <= chBlue) 01210 return(data[x + y * width] / 255.0); 01211 else 01212 return(0.0); 01213 } 01214 01215 Void ByteImage::SetSpan(Int row, Int start, Int length, const Colour *src) 01216 { 01217 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01218 "(ByteImage::SetSpan) illegal span access."); 01219 Int i; 01220 Byte *p = data + row * width + start; 01221 const Colour *cp = src; 01222 01223 for (i = 0; i < length; i++, p++, cp++) 01224 *p = ColourToByte(*cp); 01225 } 01226 01227 Void ByteImage::GetSpan(Int row, Int start, Int length, Colour *dst) const 01228 { 01229 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01230 "(ByteImage::GetSpan) illegal span access."); 01231 Int i; 01232 Byte *p = data + row * width + start; 01233 Colour *cp = dst; 01234 01235 for (i = 0; i < length; i++, p++, cp++) 01236 *cp = ByteToColour(*p); 01237 } 01238 01239 Void ByteImage::SetByteSpan(Int row, Int start, Int length, 01240 const Byte *src, ImgChannel channel) 01241 { 01242 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01243 "(ByteImage::SetByteSpan) illegal span access."); 01244 01245 memcpy(data + row * width + start, src, length); 01246 } 01247 01248 Void ByteImage::GetByteSpan(Int row, Int start, Int length, 01249 Byte *dst, ImgChannel channel) const 01250 { 01251 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01252 "(ByteImage::GetByteSpan) illegal span access."); 01253 01254 memcpy(dst, data + row * width + start, length); 01255 } 01256 01257 01258 // --- RGBEImage methods ------------------------------------------------------ 01259 01260 Void RGBEImage::SetSize(Int width, Int height) 01261 { 01262 delete[] data; 01263 data = new RGBEPixel[width * height]; 01264 SELF.width = width; 01265 SELF.height = height; 01266 } 01267 01268 Void RGBEImage::Clear(const Colour &c) 01269 { 01270 RGBEPixel *p = data, bc = ColourToRGBE(c); 01271 Int i; 01272 01273 for (i = 0; i < width * height; i++, p++) 01274 *p = bc; 01275 } 01276 01277 Void RGBEImage::SetPixel(Int x, Int y, const Colour &c) 01278 { 01279 Assert(x >= 0 && y >= 0 && x < width && y < height, 01280 "illegal pixel access."); 01281 data[x + y * width] = ColourToRGBE(c); 01282 } 01283 01284 Colour RGBEImage::GetPixel(Int x, Int y) const 01285 { 01286 Assert(x >= 0 && y >= 0 && x < width && y < height, 01287 "illegal pixel access."); 01288 return(RGBEToColour(data[x + y * width])); 01289 } 01290 01291 Void RGBEImage::SetSpan(Int row, Int start, Int length, const Colour *src) 01292 { 01293 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01294 "(RGBEImage::SetSpan) illegal span access."); 01295 Int i; 01296 RGBEPixel *p = data + row * width + start; 01297 const Colour *cp = src; 01298 01299 for (i = 0; i < length; i++, p++, cp++) 01300 *p = ColourToRGBE(*cp); 01301 } 01302 01303 Void RGBEImage::GetSpan(Int row, Int start, Int length, Colour *dst) const 01304 { 01305 Assert(row >= 0 && row < height && start >= 0 && start + length <= width, 01306 "(RGBEImage::GetSpan) illegal span access."); 01307 Int i; 01308 RGBEPixel *p = data + row * width + start; 01309 Colour *cp = dst; 01310 01311 for (i = 0; i < length; i++, p++, cp++) 01312 *cp = RGBEToColour(*p); 01313 } 01314 01315 const Int kRGBE_Excess = 128; 01316 01317 Colour RGBEToColour(RGBEPixel rgbe) 01318 { 01319 Colour result; 01320 Double f; 01321 01322 if (rgbe.ch[3] == 0) 01323 return(cBlack); 01324 01325 f = ldexp(1.0, (Int) rgbe.ch[3] - (kRGBE_Excess + 8)); 01326 01327 result[0] = f * (rgbe.ch[0] + 0.5); 01328 result[1] = f * (rgbe.ch[1] + 0.5); 01329 result[2] = f * (rgbe.ch[2] + 0.5); 01330 01331 return(result); 01332 } 01333 01334 RGBEPixel ColourToRGBE(const Colour &c) 01335 { 01336 RGBEPixel result; 01337 Int e; 01338 Double d; 01339 01340 d = MaxCmpt(c); 01341 if (d <= 1e-32) 01342 { 01343 *((UInt32*) result.ch) = 0; 01344 } 01345 else 01346 { 01347 d = frexp(d, &e) * 256.0 / d; 01348 01349 result.ch[0] = (Byte) (c[0] * d); 01350 result.ch[1] = (Byte) (c[1] * d); 01351 result.ch[2] = (Byte) (c[2] * d); 01352 result.ch[3] = e + kRGBE_Excess; 01353 } 01354 01355 return(result); 01356 } 01357 01358 RGBEPixel RGBEMult(RGBEPixel rgbe, Double s) 01359 { 01360 Colour temp; 01361 Double d, f; 01362 Int e; 01363 RGBEPixel result; 01364 01365 *((UInt32*) result.ch) = 0; 01366 01367 if (rgbe.ch[3] == 0) 01368 return(result); 01369 01370 f = ldexp(1.0, (Int) rgbe.ch[3] - (kRGBE_Excess + 8)); 01371 01372 temp[0] = f * (rgbe.ch[0] + 0.5); 01373 temp[1] = f * (rgbe.ch[1] + 0.5); 01374 temp[2] = f * (rgbe.ch[2] + 0.5); 01375 01376 d = MaxCmpt(temp); 01377 if (d <= 1e-32) 01378 return(result); 01379 else 01380 { 01381 d = frexp(d, &e) * 256.0 / d; 01382 01383 result.ch[0] = (Byte) (temp[0] * d); 01384 result.ch[1] = (Byte) (temp[1] * d); 01385 result.ch[2] = (Byte) (temp[2] * d); 01386 result.ch[3] = e + kRGBE_Excess; 01387 } 01388 01389 return(result); 01390 } 01391 01392 01393 // --- ChannelImage methods --------------------------------------------------- 01394 01395 #ifdef UNFINISHED 01396 01397 ChannelImage::ChannelImage() : tag(imgChannelTag) {} 01398 { 01399 } 01400 #endif 01401 01402 01403 Int RGBEImage::SavePIC(const Char *filename) 01404 { 01405 Int i, j; 01406 FILE *pic; 01407 RGBEPixel *p, *buffer = new RGBEPixel[width]; 01408 01409 if (!buffer) 01410 return(-1); 01411 pic = fopen(filename, "w"); 01412 if (!pic) 01413 return(-1); 01414 01415 fprintf(pic, "#?RADIANCE\n"); 01416 fprintf(pic, "gcl_out\n"); 01417 fprintf(pic, "FORMAT=32-bit_rle_rgbe\n"); 01418 fprintf(pic, "\n"); 01419 fprintf(pic, "-Y %d +X %d\n", width, height); 01420 01421 for (j = height - 1; j >= 0; j--) 01422 { 01423 p = data + j * width; 01424 // convert to radiance 01425 for (i = 0; i < width; i++) 01426 buffer[i] = RGBEMult(p[i], 1.0 / 179.0); 01427 fwrite(buffer, sizeof(RGBEPixel), width, pic); 01428 } 01429 01430 delete[] buffer; 01431 01432 return(fclose(pic)); 01433 } 01434 01435 Int RGBEImage::LoadPIC(const Char *filename) 01436 { 01437 Int i, j; 01438 Int x, y, level; 01439 FILE *pic; 01440 RGBEPixel *p, *buffer; 01441 01442 pic = fopen(filename, "r"); 01443 if (!pic) 01444 return(-1); 01445 01446 if (fscanf(pic, 01447 "#?RADIANCE\n" 01448 "gcl_out\n" 01449 "FORMAT=32-bit_rle_rgbe\n\n" 01450 "-Y %d +X %d\n", &x, &y) != 2) 01451 { 01452 _Warning("Couldn't read pic file: bad format/not gcl_out\n"); 01453 fclose(pic); 01454 return(-1); 01455 } 01456 01457 SetSize(x, y); 01458 01459 for (j = height - 1; j >= 0; j--) 01460 { 01461 p = data + j * width; 01462 fread(p, sizeof(RGBEPixel), width, pic); 01463 } 01464 01465 return(fclose(pic)); 01466 }