00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <stdio.h>
00021 #include <errno.h>
00022 #include <unistd.h>
00023 #include <iostream>
00024 #include <string>
00025
00026 extern "C"
00027 {
00028 #include <gif_lib.h>
00029 }
00030
00031 #include <archon/util/mutex.H>
00032 #include <archon/util/text.H>
00033 #include <archon/util/random.H>
00034 #include <archon/util/image.H>
00035
00036 using namespace std;
00037
00038 namespace Archon
00039 {
00040 namespace Utilities
00041 {
00042
00043 OUCH OUCH OUCH It seems libungif is not thread-safe after all due to its global error code storage.
00044
00057 struct FormatLibgif: Image::Format
00058 {
00059 string getSpecifier() const
00060 {
00061 return "gif";
00062 }
00063
00064 bool checkSignature(Ref<Stream::Reader> r) const
00065 {
00066 LoadContext c(r, 0, 0);
00067 LoadWrapper w;
00068 w.g = DGifOpen(&c, readCallback);
00069 return w.g;
00070 }
00071
00072 bool checkSuffix(string s) const
00073 {
00074 return s == "gif";
00075 }
00076
00077 Image load(Ref<Stream::Reader> reader, Image::ProgressTracker *tracker,
00078 Logger *logger) const
00079 throw(Image::InvalidFormatException,
00080 IOException, UnexpectedException)
00081 {
00082 LoadContext c(r, tracker, logger);
00083 LoadWrapper w;
00084 w.g = DGifOpen(&c, readCallback);
00085 if(!w.g) error(&context);
00086
00087 if(DGifSlurp(w.g) != GIF_OK) loadError(&context);
00088 if(w.g->ImageCount<1)
00089 {
00090 if(logger) logger->log("No images in GIF file");
00091 ARCHON_THROW1(Image::FormatException, "");
00092 }
00093
00094 SavedImage *image = w.g->SavedImages;
00095
00096 if(image->ImageDesc.Left + image->ImageDesc.Width > w.g->SWidth ||
00097 image->ImageDesc.Top + image->ImageDesc.Height > w.g->SHeight)
00098 {
00099 if(logger) logger->log("Image does not fit on canvas");
00100 ARCHON_THROW1(Image::FormatException, "");
00101 }
00102
00103 ColorMapObject *palette = image->ImageDesc.ColorMap;
00104 if(!palette) palette = w.g->SColorMap;
00105 if(!palette)
00106 {
00107 if(logger) logger->log("No palette in GIF file");
00108 ARCHON_THROW1(Image::FormatException, "");
00109 }
00110 if(w.g->SBackGroundColor >= palette->ColorCount)
00111 {
00112 if(logger) logger->log("Background color is not in palette");
00113 ARCHON_THROW1(Image::FormatException, "");
00114 }
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137 int transparentIndex = -1;
00138 for(int i=0; i<image->ExtensionBlockCount; ++i)
00139 {
00140 ExtensionBlock *ext = image->ExtensionBlocks + i;
00141 int n = ext->ByteCount;
00142 switch(ext->Function)
00143 {
00144 case COMMENT_EXT_FUNC_CODE:
00145 {
00146 int m=0;
00147 while(m<n && ext->Bytes[m]) ++m;
00148 if(m && !comment.empty()) comment += "\n";
00149 comment += string(ext->Bytes, m);
00150 }
00151 break;
00152 case GRAPHICS_EXT_FUNC_CODE:
00153 {
00154 if(n>=4 && ext->Bytes[0]&1)
00155 transparentIndex = static_cast<unsigned char>(ext->Bytes[3]);
00156 }
00157 break;
00158 }
00159 }
00160 bool transparent = transparentIndex >= 0;
00161
00162
00163 width = w.g->SWidth;
00164 height = w.g->SHeight;
00165 bitsPerComponent = 8;
00166 components = transparent ? components_rgba : components_rgb;
00167 charsPerPixel = (bitsPerComponent*components+7)/8;
00168 charsPerRow = charsPerPixel*width;
00169
00170 DataHolder dataHolder(new unsigned char[charsPerRow*height]);
00171
00172
00173 {
00174 GifColorType *color = palette->Colors + w.g->SBackGroundColor;
00175 unsigned char alpha = static_cast<unsigned char>
00176 (w.g->SBackGroundColor == transparentIndex ? 0 : 255);
00177
00178 unsigned char *p = dataHolder.ptr;
00179 for(unsigned x=0; x<width; ++x)
00180 {
00181 *p++ = static_cast<unsigned char>(color->Red);
00182 *p++ = static_cast<unsigned char>(color->Green);
00183 *p++ = static_cast<unsigned char>(color->Blue);
00184 if(transparent) *p++ = alpha;
00185 }
00186 for(unsigned y=1; y<height; ++y) memcpy(dataHolder.ptr+y*charsPerRow, dataHolder.ptr, charsPerRow);
00187 }
00188
00189
00190 char *src = image->RasterBits;
00191 unsigned char *dst = dataHolder.ptr + ((height-1-image->ImageDesc.Top)*width + image->ImageDesc.Left)*charsPerPixel;
00192 if(image->ImageDesc.Interlace)
00193 {
00194 int offset[] = { 0, 4, 2, 1 };
00195 int step[] = { 8, 8, 4, 2 };
00196 for(unsigned i=0; i<4; ++i) for(int y=offset[i]; y<image->ImageDesc.Height; y+=step[i])
00197 {
00198 unsigned char *d = dst - charsPerRow*y;
00199 for(int x=0; x<image->ImageDesc.Width; ++x)
00200 {
00201 if(static_cast<unsigned char>(*src) >= palette->ColorCount)
00202 {
00203 if(logger) logger->log("Pixel color is not in palette");
00204 ARCHON_THROW1(Image::FormatException, "");
00205 }
00206 int s = static_cast<unsigned char>(*src);
00207 GifColorType *color = palette->Colors + s;
00208 *d++ = static_cast<unsigned char>(color->Red);
00209 *d++ = static_cast<unsigned char>(color->Green);
00210 *d++ = static_cast<unsigned char>(color->Blue);
00211 if(transparent) *d++ = static_cast<unsigned char>
00212 (s == transparentIndex ? 0 : 255);
00213 ++src;
00214 }
00215 }
00216 }
00217 else
00218 {
00219 for(int y=0; y<image->ImageDesc.Height; ++y)
00220 {
00221 unsigned char *d = dst;
00222 for(int x=0; x<image->ImageDesc.Width; ++x)
00223 {
00224 if(static_cast<unsigned char>(*src) >= palette->ColorCount)
00225 {
00226 if(logger) logger->log("Pixel color is not in palette");
00227 ARCHON_THROW1(Image::FormatException, "");
00228 }
00229 int s = static_cast<unsigned char>(*src);
00230 GifColorType *color = palette->Colors + s;
00231 *d++ = static_cast<unsigned char>(color->Red);
00232 *d++ = static_cast<unsigned char>(color->Green);
00233 *d++ = static_cast<unsigned char>(color->Blue);
00234 if(transparent) *d++ = static_cast<unsigned char>
00235 (s == transparentIndex ? 0 : 255);
00236 ++src;
00237 }
00238 dst -= charsPerRow;
00239 }
00240 }
00241
00242 data = dataHolder.ptr;
00243 dataHolder.ptr = 0;
00244 }
00245
00246 private:
00247
00248 struct Context
00249 {
00250 Image::ProgressTracker *tracker;
00251 Logger *logger;
00252 int errorType;
00253
00254
00255
00256 string errorMessage;
00257 string errorLocation;
00258 Context(Image::ProgressTracker *tracker, Logger *logger):
00259 tracker(tracker), logger(logger), errorType(0) {}
00260 };
00261
00262 struct LoadContext: Context
00263 {
00264 Ref<Stream::Reader> reader;
00265 LoadContext(Ref<Stream::Reader> reader, Image::ProgressTracker *tracker,
00266 Logger *logger): Context(tracker, logger), reader(reader) {}
00267 };
00268
00269 struct SaveContext: Context
00270 {
00271 Ref<Stream::Writer> writer;
00272 SaveContext(Ref<Stream::Writer> writer, Image::ProgressTracker *tracker,
00273 Logger *logger): Context(tracker, logger), writer(writer) {}
00274 };
00275
00276 struct LoadWrapper
00277 {
00278 GifFileType *g;
00279 LoadWrapper(): g(0) {}
00280 ~LoadWrapper()
00281 {
00282 if(!g) return;
00283 LoadContext *context = static_cast<LoadContext *>(g->UserData);
00284 if(DGifCloseFile(g) != GIF_OK) loadError(context);
00285 }
00286 };
00287
00288 static int readCallback(GifFileType *g, GifByteType *buffer, int size) throw()
00289 {
00290 LoadContext *c = static_cast<LoadContext *>(g->UserData);
00291 try
00292 {
00293 return c->reader->readAll(reinterpret_cast<char *>(buffer), size);
00294 }
00295 catch(ThreadTerminatedException &e)
00296 {
00297 c->errorType = 1;
00298 c->errorLocation = e.getLocation();
00299 }
00300 catch(IOException &e)
00301 {
00302 c->errorType = 2;
00303 c->errorLocation = e.getLocation();
00304 c->errorMessage = e.getMessage();
00305 }
00306 catch(UnexpectedException &e)
00307 {
00308 c->errorType = 3;
00309 c->errorLocation = e.getLocation();
00310 c->errorMessage = e.getMessage();
00311 }
00312
00313 return -1;
00314 }
00315
00316
00317
00318
00319
00320
00321
00322 const char *gifErrorMessage(int i)
00323 {
00324 switch(i)
00325 {
00326 case E_GIF_ERR_OPEN_FAILED:
00327 return "Failed to open given file";
00328 case E_GIF_ERR_WRITE_FAILED:
00329 return "Failed to Write to given file";
00330 case E_GIF_ERR_HAS_SCRN_DSCR:
00331 return "Screen Descriptor already been set";
00332 case E_GIF_ERR_HAS_IMAG_DSCR:
00333 return "Image Descriptor is still active";
00334 case E_GIF_ERR_NO_COLOR_MAP:
00335 return "Neither Global Nor Local color map";
00336 case E_GIF_ERR_DATA_TOO_BIG:
00337 return "#Pixels bigger than Width * Height";
00338 case E_GIF_ERR_NOT_ENOUGH_MEM:
00339 return "Fail to allocate required memory";
00340 case E_GIF_ERR_DISK_IS_FULL:
00341 return "Write failed (disk full?)";
00342 case E_GIF_ERR_CLOSE_FAILED:
00343 return "Failed to close given file";
00344 case E_GIF_ERR_NOT_WRITEABLE:
00345 return "Given file was not opened for write";
00346 case D_GIF_ERR_OPEN_FAILED:
00347 return "Failed to open given file";
00348 case D_GIF_ERR_READ_FAILED:
00349 return "Failed to Read from given file";
00350 case D_GIF_ERR_NOT_GIF_FILE:
00351 return "Given file is NOT GIF file";
00352 case D_GIF_ERR_NO_SCRN_DSCR:
00353 return "No Screen Descriptor detected";
00354 case D_GIF_ERR_NO_IMAG_DSCR:
00355 return "No Image Descriptor detected";
00356 case D_GIF_ERR_NO_COLOR_MAP:
00357 return "Neither Global Nor Local color map";
00358 case D_GIF_ERR_WRONG_RECORD:
00359 return "Wrong record type detected";
00360 case D_GIF_ERR_DATA_TOO_BIG:
00361 return "#Pixels bigger than Width * Height";
00362 case D_GIF_ERR_NOT_ENOUGH_MEM:
00363 return "Fail to allocate required memory";
00364 case D_GIF_ERR_CLOSE_FAILED:
00365 return "Failed to close given file";
00366 case D_GIF_ERR_NOT_READABLE:
00367 return "Given file was not opened for read";
00368 case D_GIF_ERR_IMAGE_DEFECT:
00369 return "Image is defective, decoding aborted";
00370 case D_GIF_ERR_EOF_TOO_SOON:
00371 return "Image EOF detected, before image complete";
00372 default:
00373 ARCHON_THROW1(Image::InternalException, "Unexpected GIF error");
00374 }
00375 }
00376
00377 void loadError(LoadContext *context)
00378 {
00379 if(context->terminated) ARCHON_THROW(ThreadTerminatedException);
00380 if(context->logger) context->logger->log(gifErrorMessage(GifLastError()));
00381 ARCHON_THROW1(Image::FormatException, "");
00382 }
00383
00384
00385 }
00386
00387 void Image::loadGif(Ref<Stream::Reader> reader, Logger *logger,
00388 LoadTracker *loadTracker)
00389 {
00390 }
00391
00392 void Image::saveGif(Ref<Stream::Writer> writer, Logger *logger) const
00393 {
00394 ARCHON_THROW1(InternalException, "Save GIF is not yet supported");
00395 }
00396 }
00397 }