Back to index

lightning-sunbird  0.9+nobinonly
GIF2.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Chris Saari <saari@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 /*
00040 The Graphics Interchange Format(c) is the copyright property of CompuServe
00041 Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
00042 enhance, alter, modify or change in any way the definition of the format.
00043 
00044 CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
00045 license for the use of the Graphics Interchange Format(sm) in computer
00046 software; computer software utilizing GIF(sm) must acknowledge ownership of the
00047 Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
00048 User and Technical Documentation. Computer software utilizing GIF, which is
00049 distributed or may be distributed without User or Technical Documentation must
00050 display to the screen or printer a message acknowledging ownership of the
00051 Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
00052 this case, the acknowledgement may be displayed in an opening screen or leading
00053 banner, or a closing screen or trailing banner. A message such as the following
00054 may be used:
00055 
00056     "The Graphics Interchange Format(c) is the Copyright property of
00057     CompuServe Incorporated. GIF(sm) is a Service Mark property of
00058     CompuServe Incorporated."
00059 
00060 For further information, please contact :
00061 
00062     CompuServe Incorporated
00063     Graphics Technology Department
00064     5000 Arlington Center Boulevard
00065     Columbus, Ohio  43220
00066     U. S. A.
00067 
00068 CompuServe Incorporated maintains a mailing list with all those individuals and
00069 organizations who wish to receive copies of this document when it is corrected
00070 or revised. This service is offered free of charge; please provide us with your
00071 mailing address.
00072 */
00073 
00074 #include <stddef.h>
00075 #include "prtypes.h"
00076 #include "prmem.h"
00077 #include "prlog.h"
00078 #include "GIF2.h"
00079 
00080 #include "nsGIFDecoder2.h"
00081 
00082 /* Gather n characters from the input stream and then enter state s. */
00083 #define GETN(n,s)                    \
00084   PR_BEGIN_MACRO                     \
00085     gs->state = gif_gather;          \
00086     gs->gather_request_size = (n);   \
00087     gs->post_gather_state = s;       \
00088   PR_END_MACRO
00089 
00090 /* Get a 16-bit value stored in little-endian format */
00091 #define GETINT16(p)   ((p)[1]<<8|(p)[0])
00092 
00093 /* Get a 32-bit value stored in little-endian format */
00094 #define GETINT32(p)   (((p)[3]<<24) | ((p)[2]<<16) | ((p)[1]<<8) | ((p)[0]))
00095 
00096 //******************************************************************************
00097 /*  binary block Allocate and Concatenate
00098  *
00099  *   destination_length  is the length of the existing block
00100  *   source_length   is the length of the block being added to the
00101  *   destination block
00102  */
00103 static char *il_BACat (char **destination,
00104                        size_t destination_length,
00105                        const char *source,
00106                        size_t source_length)
00107 {
00108   if (source) {
00109     if (*destination) {
00110       *destination = (char *) PR_REALLOC (*destination,
00111                                           destination_length + source_length);
00112       if (*destination == nsnull)
00113         return (nsnull);
00114 
00115       memmove(*destination + destination_length, source, source_length);
00116     }
00117     else {
00118       *destination = (char *) PR_MALLOC (source_length);
00119       if (*destination == nsnull)
00120         return (nsnull);
00121 
00122       memcpy(*destination, source, source_length);
00123     }
00124   }
00125 
00126   return *destination;
00127 }
00128 
00129 #undef BlockAllocCat
00130 #define BlockAllocCat(dest, dest_length, src, src_length) \
00131   il_BACat(&(dest), dest_length, src, src_length)
00132 
00133 //******************************************************************************
00134 // Send the data to the display front-end.
00135 static void output_row(gif_struct *gs)
00136 {
00137   int width, drow_start, drow_end;
00138 
00139   drow_start = drow_end = gs->irow;
00140 
00141   /*
00142    * Haeberli-inspired hack for interlaced GIFs: Replicate lines while
00143    * displaying to diminish the "venetian-blind" effect as the image is
00144    * loaded. Adjust pixel vertical positions to avoid the appearance of the
00145    * image crawling up the screen as successive passes are drawn.
00146    */
00147   if (gs->progressive_display && gs->interlaced && (gs->ipass < 4)) {
00148     PRUintn row_dup = 0, row_shift = 0;
00149 
00150     switch (gs->ipass) {
00151     case 1:
00152       row_dup = 7;
00153       row_shift = 3;
00154       break;
00155     case 2:
00156       row_dup = 3;
00157       row_shift = 1;
00158       break;
00159     case 3:
00160       row_dup = 1;
00161       row_shift = 0;
00162       break;
00163     default:
00164       break;
00165     }
00166 
00167     drow_start -= row_shift;
00168     drow_end = drow_start + row_dup;
00169 
00170     /* Extend if bottom edge isn't covered because of the shift upward. */
00171     if (((gs->height - 1) - drow_end) <= row_shift)
00172       drow_end = gs->height - 1;
00173 
00174     /* Clamp first and last rows to upper and lower edge of image. */
00175     if (drow_start < 0)
00176       drow_start = 0;
00177     if ((PRUintn)drow_end >= gs->height)
00178       drow_end = gs->height - 1;
00179   }
00180 
00181   /* Protect against too much image data */
00182   if ((PRUintn)drow_start >= gs->height) {
00183     NS_WARNING("GIF2.cpp::output_row - too much image data");
00184     return;
00185   }
00186 
00187   /* Check for scanline below edge of logical screen */
00188   if ((gs->y_offset + gs->irow) < gs->screen_height) {
00189     /* Clip if right edge of image exceeds limits */
00190     if ((gs->x_offset + gs->width) > gs->screen_width)
00191       width = gs->screen_width - gs->x_offset;
00192     else
00193       width = gs->width;
00194 
00195     if (width > 0)
00196       /* Decoded data available callback */
00197       nsGIFDecoder2::HaveDecodedRow(
00198         gs->clientptr,
00199         gs->rowbuf,      // Pointer to single scanline temporary buffer
00200         drow_start,      // Row number
00201         drow_end - drow_start + 1, // Number of times to duplicate the row?
00202         gs->ipass);      // interlace pass (1-4)
00203   }
00204 
00205   gs->rowp = gs->rowbuf;
00206 
00207   if (!gs->interlaced)
00208     gs->irow++;
00209   else {
00210     do {
00211       switch (gs->ipass)
00212       {
00213         case 1:
00214           gs->irow += 8;
00215           if (gs->irow >= gs->height) {
00216             gs->ipass++;
00217             gs->irow = 4;
00218           }
00219           break;
00220 
00221         case 2:
00222           gs->irow += 8;
00223           if (gs->irow >= gs->height) {
00224             gs->ipass++;
00225             gs->irow = 2;
00226           }
00227           break;
00228 
00229         case 3:
00230           gs->irow += 4;
00231           if (gs->irow >= gs->height) {
00232             gs->ipass++;
00233             gs->irow = 1;
00234           }
00235           break;
00236 
00237         case 4:
00238           gs->irow += 2;
00239           if (gs->irow >= gs->height){
00240             gs->ipass++;
00241             gs->irow = 0;
00242           }
00243           break;
00244 
00245         default:
00246           break;
00247       }
00248     } while (gs->irow > (gs->height - 1));
00249   }
00250 }
00251 
00252 //******************************************************************************
00253 /* Perform Lempel-Ziv-Welch decoding */
00254 static int do_lzw(gif_struct *gs, const PRUint8 *q)
00255 {
00256   int code;
00257   int incode;
00258   const PRUint8 *ch;
00259 
00260   /* Copy all the decoder state variables into locals so the compiler
00261    * won't worry about them being aliased.  The locals will be homed
00262    * back into the GIF decoder structure when we exit.
00263    */
00264   int avail       = gs->avail;
00265   int bits        = gs->bits;
00266   int codesize    = gs->codesize;
00267   int codemask    = gs->codemask;
00268   int count       = gs->count;
00269   int oldcode     = gs->oldcode;
00270   int clear_code  = gs->clear_code;
00271   PRUint8 firstchar = gs->firstchar;
00272   PRInt32 datum     = gs->datum;
00273   PRUint16 *prefix  = gs->prefix;
00274   PRUint8 *stackp   = gs->stackp;
00275   PRUint8 *suffix   = gs->suffix;
00276   PRUint8 *stack    = gs->stack;
00277   PRUint8 *rowp     = gs->rowp;
00278   PRUint8 *rowend   = gs->rowend;
00279   PRUintn rows_remaining = gs->rows_remaining;
00280 
00281   if (rowp == rowend)
00282     return 0;
00283 
00284 #define OUTPUT_ROW(gs)                                                  \
00285   PR_BEGIN_MACRO                                                        \
00286     output_row(gs);                                                     \
00287     rows_remaining--;                                                   \
00288     rowp = gs->rowp;                                                    \
00289     if (!rows_remaining)                                                \
00290       goto END;                                                         \
00291   PR_END_MACRO
00292 
00293   for (ch = q; count-- > 0; ch++)
00294   {
00295     /* Feed the next byte into the decoder's 32-bit input buffer. */
00296     datum += ((int32) *ch) << bits;
00297     bits += 8;
00298 
00299     /* Check for underflow of decoder's 32-bit input buffer. */
00300     while (bits >= codesize)
00301     {
00302       /* Get the leading variable-length symbol from the data stream */
00303       code = datum & codemask;
00304       datum >>= codesize;
00305       bits -= codesize;
00306 
00307       /* Reset the dictionary to its original state, if requested */
00308       if (code == clear_code) {
00309         codesize = gs->datasize + 1;
00310         codemask = (1 << codesize) - 1;
00311         avail = clear_code + 2;
00312         oldcode = -1;
00313         continue;
00314       }
00315 
00316       /* Check for explicit end-of-stream code */
00317       if (code == (clear_code + 1)) {
00318         /* end-of-stream should only appear after all image data */
00319         if (rows_remaining != 0)
00320           return -1;
00321         return 0;
00322       }
00323 
00324       if (oldcode == -1) {
00325         *rowp++ = suffix[code];
00326         if (rowp == rowend)
00327           OUTPUT_ROW(gs);
00328 
00329         firstchar = oldcode = code;
00330         continue;
00331       }
00332 
00333       incode = code;
00334       if (code >= avail) {
00335         *stackp++ = firstchar;
00336         code = oldcode;
00337 
00338         if (stackp == stack + MAX_BITS)
00339           return -1;
00340       }
00341 
00342       while (code >= clear_code)
00343       {
00344         if (code == prefix[code])
00345           return -1;
00346 
00347         *stackp++ = suffix[code];
00348         code = prefix[code];
00349 
00350         if (stackp == stack + MAX_BITS || code >= MAX_BITS)
00351           return -1;
00352       }
00353 
00354       *stackp++ = firstchar = suffix[code];
00355 
00356       /* Define a new codeword in the dictionary. */
00357       if (avail < 4096) {
00358         prefix[avail] = oldcode;
00359         suffix[avail] = firstchar;
00360         avail++;
00361 
00362         /* If we've used up all the codewords of a given length
00363          * increase the length of codewords by one bit, but don't
00364          * exceed the specified maximum codeword size of 12 bits.
00365          */
00366         if (((avail & codemask) == 0) && (avail < 4096)) {
00367           codesize++;
00368           codemask += avail;
00369         }
00370       }
00371       oldcode = incode;
00372 
00373         /* Copy the decoded data out to the scanline buffer. */
00374       do {
00375         *rowp++ = *--stackp;
00376         if (rowp == rowend) {
00377           OUTPUT_ROW(gs);
00378         }
00379       } while (stackp > stack);
00380     }
00381   }
00382 
00383   END:
00384 
00385   /* Home the local copies of the GIF decoder state variables */
00386   gs->avail = avail;
00387   gs->bits = bits;
00388   gs->codesize = codesize;
00389   gs->codemask = codemask;
00390   gs->count = count;
00391   gs->oldcode = oldcode;
00392   gs->firstchar = firstchar;
00393   gs->datum = datum;
00394   gs->stackp = stackp;
00395   gs->rowp = rowp;
00396   gs->rows_remaining = rows_remaining;
00397 
00398   return 0;
00399 }
00400 
00401 /*******************************************************************************
00402  * setup for gif_struct decoding
00403  */
00404 PRBool GIFInit(gif_struct* gs, void* aClientData)
00405 {
00406   NS_ASSERTION(gs, "Got null argument");
00407   if (!gs)
00408     return PR_FALSE;
00409 
00410   // Clear out the structure, excluding the arrays
00411   memset(gs, 0, sizeof(gif_struct));
00412   gs->clientptr = aClientData;
00413 
00414   gs->state = gif_init;
00415   gs->post_gather_state = gif_error;
00416   gs->gathered = 0;
00417 
00418   return PR_TRUE;
00419 }
00420 
00421 /* Maximum # of bytes to read ahead while waiting for delay_time to expire.
00422    We no longer limit this number to remain within WIN16 malloc limitations
00423    of 0xffff */
00424 
00425 #define MAX_READ_AHEAD  (0xFFFFFFL)
00426 
00427 //******************************************************************************
00428 PRBool gif_write_ready(const gif_struct* gs)
00429 {
00430   if (!gs)
00431     return PR_FALSE;
00432 
00433   return (gs->gathered < MAX_READ_AHEAD);
00434 }
00435 
00436 /******************************************************************************/
00437 /*
00438  * process data arriving from the stream for the gif decoder
00439  */
00440 
00441 PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
00442 {
00443   if (!gs)
00444     return PR_FAILURE;
00445 
00446   /* If we fail, some upstream data provider ignored the
00447      zero return value from il_gif_write_ready() which says not to
00448      send any more data to this stream until the delay timeout fires. */
00449   if ((len != 0) && (gs->gathered >= MAX_READ_AHEAD))
00450     return PR_FAILURE;
00451 
00452   const PRUint8 *q, *p = buf, *ep = buf + len;
00453 
00454   q = nsnull;                   /* Initialize to shut up gcc warnings */
00455 
00456   while (p <= ep) {
00457     switch (gs->state)
00458     {
00459     case gif_lzw:
00460       if (do_lzw(gs, q) < 0) {
00461         gs->state = gif_error;
00462         break;
00463       }
00464       GETN(1, gif_sub_block);
00465       break;
00466 
00467     case gif_lzw_start:
00468     {
00469       /* Initialize LZW parser/decoder */
00470       gs->datasize = *q;
00471       if (gs->datasize >= MAX_LZW_BITS) {
00472         gs->state = gif_error;
00473         break;
00474       }
00475 
00476       gs->clear_code = 1 << gs->datasize;
00477       gs->avail = gs->clear_code + 2;
00478       gs->oldcode = -1;
00479       gs->codesize = gs->datasize + 1;
00480       gs->codemask = (1 << gs->codesize) - 1;
00481 
00482       gs->datum = gs->bits = 0;
00483 
00484       if (gs->clear_code >= MAX_BITS) {
00485         gs->state = gif_error;
00486         break;
00487       }
00488 
00489       /* init the tables */
00490       for (int i = 0; i < gs->clear_code; i++)
00491         gs->suffix[i] = i;
00492 
00493       gs->stackp = gs->stack;
00494 
00495       GETN(1, gif_sub_block);
00496     }
00497     break;
00498 
00499     /* We're positioned at the very start of the file. */
00500     case gif_init:
00501     {
00502       GETN(3, gif_type);
00503       break;
00504     }
00505 
00506     /* All GIF files begin with "GIF87a" or "GIF89a" */
00507     case gif_type:
00508     {
00509       if (strncmp((char*)q, "GIF", 3)) {
00510         gs->state = gif_error;
00511         break;
00512       }
00513       GETN(3, gif_version);
00514     }
00515     break;
00516 
00517     case gif_version:
00518     {
00519       if (!strncmp((char*)q, "89a", 3)) {
00520         gs->version = 89;
00521       } else if (!strncmp((char*)q, "87a", 3)) {
00522         gs->version = 87;
00523       } else {
00524         gs->state = gif_error;
00525         break;
00526       }
00527       GETN(7, gif_global_header);
00528     }
00529     break;
00530 
00531     case gif_global_header:
00532     {
00533       /* This is the height and width of the "screen" or
00534        * frame into which images are rendered.  The
00535        * individual images can be smaller than the
00536        * screen size and located with an origin anywhere
00537        * within the screen.
00538        */
00539 
00540       gs->screen_width = GETINT16(q);
00541       gs->screen_height = GETINT16(q + 2);
00542 
00543       gs->screen_bgcolor = q[5];
00544 
00545       gs->global_colormap_size = 2<<(q[4]&0x07);
00546 
00547       // XXX make callback
00548       nsGIFDecoder2::BeginGIF(
00549         gs->clientptr,
00550         gs->screen_width,
00551         gs->screen_height,
00552         gs->screen_bgcolor);
00553 
00554       if (q[4] & 0x80) /* global map */
00555         /* 3 bytes for each entry in the global colormap */
00556         GETN(gs->global_colormap_size*3, gif_global_colormap);
00557       else
00558         GETN(1, gif_image_start);
00559 
00560       // q[6] = Pixel Aspect Ratio
00561       //   Not used
00562       //   float aspect = (float)((q[6] + 15) / 64.0);
00563     }
00564     break;
00565 
00566     case gif_global_colormap:
00567     {
00568       memcpy(gs->global_colormap, q, 3 * gs->global_colormap_size);
00569 
00570       GETN(1, gif_image_start);
00571     }
00572     break;
00573 
00574     case gif_image_start:
00575     {
00576       if (*q == ';') { /* terminator */
00577         gs->state = gif_done;
00578         break;
00579       }
00580 
00581       if (*q == '!') { /* extension */
00582         GETN(2, gif_extension);
00583         break;
00584       }
00585 
00586       /* If we get anything other than ',' (image separator), '!'
00587        * (extension), or ';' (trailer), there is extraneous data
00588        * between blocks. The GIF87a spec tells us to keep reading
00589        * until we find an image separator, but GIF89a says such
00590        * a file is corrupt. We follow GIF89a and bail out. */
00591       if (*q != ',') {
00592         if (gs->images_decoded > 0) {
00593           /* The file is corrupt, but one or more images have
00594            * been decoded correctly. In this case, we proceed
00595            * as if the file were correctly terminated and set
00596            * the state to gif_done, so the GIF will display.
00597            */
00598           gs->state = gif_done;
00599         } else {
00600           /* No images decoded, there is nothing to display. */
00601           gs->state = gif_error;
00602         }
00603         break;
00604       } else
00605         GETN(9, gif_image_header);
00606     }
00607     break;
00608 
00609     case gif_extension:
00610     {
00611       int len = gs->count = q[1];
00612       gstate es = gif_skip_block;
00613 
00614       switch (*q)
00615       {
00616       case 0xf9:
00617         es = gif_control_extension;
00618         break;
00619 
00620       case 0x01:
00621         // ignoring plain text extension
00622         break;
00623 
00624       case 0xff:
00625         es = gif_application_extension;
00626         break;
00627 
00628       case 0xfe:
00629         es = gif_consume_comment;
00630         break;
00631       }
00632 
00633       if (len)
00634         GETN(len, es);
00635       else
00636         GETN(1, gif_image_start);
00637     }
00638     break;
00639 
00640     case gif_consume_block:
00641       if (!*q)
00642         GETN(1, gif_image_start);
00643       else
00644         GETN(*q, gif_skip_block);
00645     break;
00646 
00647     case gif_skip_block:
00648       GETN(1, gif_consume_block);
00649       break;
00650 
00651     case gif_control_extension:
00652     {
00653       if (*q & 0x1) {
00654         gs->tpixel = q[3];
00655         gs->is_transparent = PR_TRUE;
00656       } else {
00657         gs->is_transparent = PR_FALSE;
00658         // ignoring gfx control extension
00659       }
00660       gs->disposal_method = (gdispose)(((*q) >> 2) & 0x7);
00661       // Some specs say 3rd bit (value 4), other specs say value 3
00662       // Let's choose 3 (the more popular)
00663       if (gs->disposal_method == 4)
00664         gs->disposal_method = (gdispose)3;
00665       gs->delay_time = GETINT16(q + 1) * 10;
00666       GETN(1, gif_consume_block);
00667     }
00668     break;
00669 
00670     case gif_comment_extension:
00671     {
00672       gs->count = *q;
00673       if (gs->count)
00674         GETN(gs->count, gif_consume_comment);
00675       else
00676         GETN(1, gif_image_start);
00677     }
00678     break;
00679 
00680     case gif_consume_comment:
00681       GETN(1, gif_comment_extension);
00682     break;
00683 
00684     case gif_application_extension:
00685       /* Check for netscape application extension */
00686       if (!strncmp((char*)q, "NETSCAPE2.0", 11) ||
00687         !strncmp((char*)q, "ANIMEXTS1.0", 11))
00688         GETN(1, gif_netscape_extension_block);
00689       else
00690         GETN(1, gif_consume_block);
00691     break;
00692 
00693     /* Netscape-specific GIF extension: animation looping */
00694     case gif_netscape_extension_block:
00695       if (*q)
00696         GETN(*q, gif_consume_netscape_extension);
00697       else
00698         GETN(1, gif_image_start);
00699     break;
00700 
00701     /* Parse netscape-specific application extensions */
00702     case gif_consume_netscape_extension:
00703     {
00704       int netscape_extension = q[0] & 7;
00705 
00706       /* Loop entire animation specified # of times.  Only read the
00707          loop count during the first iteration. */
00708       if (netscape_extension == 1) {
00709         gs->loop_count = GETINT16(q + 1);
00710 
00711         /* Zero loop count is infinite animation loop request */
00712         if (gs->loop_count == 0)
00713           gs->loop_count = -1;
00714 
00715         GETN(1, gif_netscape_extension_block);
00716       }
00717       /* Wait for specified # of bytes to enter buffer */
00718       else if (netscape_extension == 2) {
00719         // Don't do this, this extension doesn't exist (isn't used at all) 
00720         // and doesn't do anything, as our streaming/buffering takes care of it all...
00721         // See: http://semmix.pl/color/exgraf/eeg24.htm
00722         GETN(1, gif_netscape_extension_block);
00723       } else
00724         gs->state = gif_error; // 0,3-7 are yet to be defined netscape
00725                                // extension codes
00726 
00727       break;
00728     }
00729 
00730     case gif_image_header:
00731     {
00732       PRUintn height, width;
00733 
00734       /* Get image offsets, with respect to the screen origin */
00735       gs->x_offset = GETINT16(q);
00736       gs->y_offset = GETINT16(q + 2);
00737 
00738       /* Get image width and height. */
00739       width  = GETINT16(q + 4);
00740       height = GETINT16(q + 6);
00741 
00742       /* Work around broken GIF files where the logical screen
00743        * size has weird width or height.  We assume that GIF87a
00744        * files don't contain animations.
00745        */
00746       if ((gs->images_decoded == 0) &&
00747           ((gs->screen_height < height) || (gs->screen_width < width) ||
00748            (gs->version == 87)))
00749       {
00750         gs->screen_height = height;
00751         gs->screen_width = width;
00752         gs->x_offset = 0;
00753         gs->y_offset = 0;
00754 
00755         nsGIFDecoder2::BeginGIF(gs->clientptr,
00756                                 gs->screen_width,
00757                                 gs->screen_height,
00758                                 gs->screen_bgcolor);
00759       }
00760 
00761       /* Work around more broken GIF files that have zero image
00762          width or height */
00763       if (!height || !width) {
00764         height = gs->screen_height;
00765         width = gs->screen_width;
00766         if (!height || !width) {
00767           gs->state = gif_error;
00768           break;
00769         }
00770       }
00771 
00772       gs->height = height;
00773       gs->width = width;
00774 
00775       nsGIFDecoder2::BeginImageFrame(gs->clientptr,
00776                                      gs->images_decoded + 1,   /* Frame number, 1-n */
00777                                      gs->x_offset,  /* X offset in logical screen */
00778                                      gs->y_offset,  /* Y offset in logical screen */
00779                                      width,
00780                                      height);
00781 
00782       /* This case will never be taken if this is the first image */
00783       /* being decoded. If any of the later images are larger     */
00784       /* than the screen size, we need to reallocate buffers.     */
00785       if (gs->screen_width < width) {
00786         /* XXX Deviant! */
00787 
00788         gs->rowbuf = (PRUint8*)PR_REALLOC(gs->rowbuf, width);
00789 
00790         if (!gs->rowbuf) {
00791           gs->state = gif_oom;
00792           break;
00793         }
00794 
00795         gs->screen_width = width;
00796         if (gs->screen_height < gs->height)
00797           gs->screen_height = gs->height;
00798 
00799       }
00800       else {
00801         if (!gs->rowbuf)
00802           gs->rowbuf = (PRUint8*)PR_MALLOC(gs->screen_width);
00803       }
00804 
00805       if (!gs->rowbuf) {
00806           gs->state = gif_oom;
00807           break;
00808       }
00809 
00810       if (q[8] & 0x40) {
00811         gs->interlaced = PR_TRUE;
00812         gs->ipass = 1;
00813       } else {
00814         gs->interlaced = PR_FALSE;
00815         gs->ipass = 0;
00816       }
00817 
00818       if (gs->images_decoded == 0) {
00819         gs->progressive_display = PR_TRUE;
00820       } else {
00821         /* Overlaying interlaced, transparent GIFs over
00822            existing image data using the Haeberli display hack
00823            requires saving the underlying image in order to
00824            avoid jaggies at the transparency edges.  We are
00825            unprepared to deal with that, so don't display such
00826            images progressively */
00827         gs->progressive_display = PR_FALSE;
00828       }
00829 
00830       /* Clear state from last image */
00831       gs->irow = 0;
00832       gs->rows_remaining = gs->height;
00833       gs->rowend = gs->rowbuf + gs->width;
00834       gs->rowp = gs->rowbuf;
00835 
00836       /* bits per pixel is 1<<((q[8]&0x07) + 1); */
00837 
00838       if (q[8] & 0x80) /* has a local colormap? */
00839       {
00840         int num_colors = 2 << (q[8] & 0x7);
00841 
00842         // If current local_colormap is not big enough, force reallocation
00843         if (num_colors > gs->local_colormap_size)
00844           PR_FREEIF(gs->local_colormap);
00845         gs->local_colormap_size = num_colors;
00846 
00847         /* Switch to the new local palette after it loads */
00848         gs->is_local_colormap_defined = PR_TRUE;
00849         GETN(gs->local_colormap_size * 3, gif_image_colormap);
00850       } else {
00851         /* Switch back to the global palette */
00852         gs->is_local_colormap_defined = PR_FALSE;
00853         GETN(1, gif_lzw_start);
00854       }
00855     }
00856     break;
00857 
00858     case gif_image_colormap:
00859     {
00860       PRUint8 *map = gs->local_colormap;
00861       if (!map) {
00862         map = gs->local_colormap = (PRUint8*)PR_MALLOC(3 * gs->local_colormap_size);
00863         if (!map) {
00864           gs->state = gif_oom;
00865           break;
00866         }
00867       }
00868 
00869       memcpy(map, q, 3 * gs->local_colormap_size);
00870 
00871       GETN(1, gif_lzw_start);
00872     }
00873     break;
00874 
00875     case gif_sub_block:
00876     {
00877       if ((gs->count = *q) != 0)
00878       /* Still working on the same image: Process next LZW data block */
00879       {
00880         /* Make sure there are still rows left. If the GIF data */
00881         /* is corrupt, we may not get an explicit terminator.   */
00882         if (gs->rows_remaining == 0) {
00883           /* This is an illegal GIF, but we remain tolerant. */
00884 #ifdef DONT_TOLERATE_BROKEN_GIFS
00885           gs->state = gif_error;
00886           break;
00887 #else
00888           GETN(1, gif_sub_block);
00889 #endif
00890         }
00891         GETN(gs->count, gif_lzw);
00892       }
00893       else
00894       /* See if there are any more images in this sequence. */
00895       {
00896         gs->images_decoded++;
00897 
00898         nsGIFDecoder2::EndImageFrame(gs->clientptr,
00899                                      gs->images_decoded,
00900                                      gs->delay_time);
00901 
00902         /* Clear state from this image */
00903         gs->is_transparent = PR_FALSE;
00904 
00905         /* An image can specify a delay time before which to display
00906            subsequent images. */
00907         if (gs->delay_time < MINIMUM_DELAY_TIME)
00908           gs->delay_time = MINIMUM_DELAY_TIME;
00909 
00910         GETN(1, gif_image_start);
00911       }
00912     }
00913     break;
00914 
00915     case gif_done:
00916       nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count);
00917       return PR_SUCCESS;
00918       break;
00919 
00920     case gif_delay:
00921     case gif_gather:
00922     {
00923       PRInt32 gather_remaining;
00924       PRInt32 request_size = gs->gather_request_size;
00925 
00926       {
00927         gather_remaining = request_size - gs->gathered;
00928 
00929         /* Do we already have enough data in the accumulation
00930            buffer to satisfy the request ?  (This can happen
00931            after we transition from the gif_delay state.) */
00932         if (gather_remaining <= 0) {
00933           gs->gathered -= request_size;
00934           q = gs->gather_head;
00935           gs->gather_head += request_size;
00936           gs->state = gs->post_gather_state;
00937           break;
00938         }
00939 
00940         /* Shift remaining data to the head of the buffer */
00941         if (gs->gathered && (gs->gather_head != gs->hold)) {
00942           memmove(gs->hold, gs->gather_head, gs->gathered);
00943           gs->gather_head = gs->hold;
00944         }
00945 
00946         /* If we add the data just handed to us by the netlib
00947            to what we've already gathered, is there enough to satisfy
00948            the current request ? */
00949         if ((ep - p) >= gather_remaining) {
00950           if (gs->gathered) { /* finish a prior gather */
00951             char *hold = (char*)gs->hold;
00952             BlockAllocCat(hold, gs->gathered, (char*)p, gather_remaining);
00953             gs->hold = (PRUint8*)hold;
00954             q = gs->gather_head = gs->hold;
00955             gs->gathered = 0;
00956           } else
00957             q = p;
00958 
00959           p += gather_remaining;
00960           gs->state = gs->post_gather_state;
00961         } else {
00962           char *hold = (char*)gs->hold;
00963           BlockAllocCat(hold, gs->gathered, (char*)p, ep - p);
00964           gs->hold = (PRUint8*)hold;
00965           gs->gather_head = gs->hold;
00966           gs->gathered += ep-p;
00967           return PR_SUCCESS;
00968         }
00969       }
00970     }
00971     break;
00972 
00973     // Handle out of memory errors
00974     case gif_oom:
00975       return PR_FAILURE;
00976 
00977     // Handle general errors
00978     case gif_error:
00979       nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count);
00980       return PR_SUCCESS;
00981 
00982     case gif_stop_animating:
00983       return PR_SUCCESS;
00984 
00985     // We shouldn't ever get here.
00986     default:
00987       break;
00988     }
00989   }
00990 
00991   return PR_SUCCESS;
00992 }
00993 
00994 //******************************************************************************
00995 /* Free up all the data structures associated with decoding a GIF */
00996 void gif_destroy(gif_struct *gs)
00997 {
00998   if (!gs)
00999     return;
01000 
01001   /* Clear any pending timeouts */
01002   if (gs->delay_time)
01003     gs->delay_time = 0;
01004 
01005   PR_FREEIF(gs->rowbuf);
01006   PR_FREEIF(gs->hold);
01007 
01008   PR_FREEIF(gs->local_colormap);
01009 }
01010