Main Page Class Hierarchy Compound List File List Compound Members File Members

Image.cc

Go to the documentation of this file.
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 }

Generated at Sat Aug 5 00:17:00 2000 for Graphics Class Library by doxygen 1.1.0 written by Dimitri van Heesch, © 1997-2000

AltStyle によって変換されたページ (->オリジナル) /