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 }