Library to control a Graphics TFT connected to 4-wire SPI - revised for the Raio RA8875 Display Controller.

Dependents:   FRDM_RA8875_mPaint RA8875_Demo RA8875_KeyPadDemo SignalGenerator ... more

Fork of SPI_TFT by Peter Drescher

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers GraphicsDisplayGIF.cpp Source File

GraphicsDisplayGIF.cpp

00001 
00002 
00003 
00004 // GIFRender.cpp : Defines the entry point for the console application.
00005 //
00006 // The code in this file was initially found online, in a tutorial.
00007 // It has been revised significantly in this derivative.
00008 // No copyright claim was found in the code.
00009 // http://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art011
00010 //
00011 
00012 #include "mbed.h"
00013 
00014 #include "GraphicsDisplay.h"
00015 
00016 
00017 //#define DEBUG "GIF_"
00018 //
00019 
00020 // INFO("Stuff to show %d", var); // new-line is automatically appended
00021 //
00022 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
00023 #define INFO(x, ...) std::printf("[INF %s %4d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00024 #define WARN(x, ...) std::printf("[WRN %s %4d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00025 #define ERR(x, ...)  std::printf("[ERR %s %4d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00026 static void HexDump(const char * title, const uint8_t * p, int count) {
00027     int i;
00028     char buf[100] = "0000: ";
00029 
00030     if (*title)
00031         INFO("%s", title);
00032     for (i = 0; i<count; ) {
00033         sprintf(buf + strlen(buf), "%02X ", *(p + i));
00034         if ((++i & 0x0F) == 0x00) {
00035             INFO("%s", buf);
00036             if (i < count)
00037                 sprintf(buf, "%04X: ", i);
00038             else
00039                 buf[0] = '\0';
00040         }
00041     }
00042     if (strlen(buf))
00043         INFO("%s", buf);
00044 }
00045 #else
00046 #define INFO(x, ...)
00047 #define WARN(x, ...)
00048 #define ERR(x, ...)
00049 #define HexDump(a, b, c)
00050 #endif
00051 
00052 
00053 #define EXTENSION_INTRODUCER   0x21
00054 #define IMAGE_DESCRIPTOR       0x2C
00055 #define TRAILER                0x3B
00056 
00057 #define GRAPHIC_CONTROL        0xF9
00058 #define APPLICATION_EXTENSION  0xFF
00059 #define COMMENT_EXTENSION      0xFE
00060 #define PLAINTEXT_EXTENSION    0x01
00061 
00062 
00063 typedef struct {
00064     unsigned char byte;
00065     int prev;
00066     int len;
00067 } dictionary_entry_t;
00068 
00069 typedef struct {
00070     unsigned char extension_code;
00071     unsigned char block_size;
00072 } extension_t;
00073 const uint16_t extension_size = 2;
00074 
00075 typedef struct {
00076     unsigned char fields;
00077     unsigned short delay_time;
00078     unsigned char transparent_color_index;
00079 } graphic_control_extension_t;
00080 const uint16_t graphic_control_extension_size = 4;
00081 
00082 typedef struct {
00083     unsigned char application_id[8];
00084     unsigned char version[3];
00085 } application_extension_t;
00086 const uint16_t application_extension_size = 11;
00087 
00088 typedef struct {
00089     unsigned short left;
00090     unsigned short top;
00091     unsigned short width;
00092     unsigned short height;
00093     unsigned char cell_width;
00094     unsigned char cell_height;
00095     unsigned char foreground_color;
00096     unsigned char background_color;
00097 } plaintext_extension_t;
00098 const uint16_t plaintext_extension_size = 12;
00099 
00100 
00101 size_t GraphicsDisplay::read_filesystem_bytes(void * buffer, int numBytes, FILE * fh) {
00102     size_t bytesRead;
00103 
00104     bytesRead = fread(buffer, 1, numBytes, fh);
00105     HexDump("mem", (const uint8_t *) buffer, bytesRead);
00106     return bytesRead;
00107 }
00108 
00109 
00110 /// uncompress_gif the data to memory
00111 ///
00112 /// @param[in] code_length is the size of this descriptor field
00113 /// @param[in] input is a pointer to the input data
00114 /// @param[in] input_length is the number of bytes in the input.
00115 /// @param[out] out is where the uncompress_gifed information is written.
00116 /// @return noerror on success, or failure code
00117 ///
00118 RetCode_t GraphicsDisplay::uncompress_gif(int code_length, const unsigned char *input, int input_length, unsigned char *out) {
00119     int i, bit;
00120     int code, prev = -1;
00121     dictionary_entry_t * dictionary;
00122     int dictionary_ind;
00123     unsigned int mask = 0x01;
00124     int reset_code_length;
00125     int clear_code; // This varies depending on code_length
00126     int stop_code;  // one more than clear code
00127     int match_len;
00128 
00129     clear_code = 1 << (code_length);
00130     stop_code = clear_code + 1;
00131     reset_code_length = code_length;
00132 
00133     // Create a dictionary large enough to hold "code_length" entries.
00134     // Once the dictionary overflows, code_length increases
00135     size_t bytes = sizeof(dictionary_entry_t) * (1 << (code_length + 1));
00136     INFO("uncompress_gif malloc(%d)", bytes);
00137     dictionary = (dictionary_entry_t *) malloc(bytes);
00138     if (dictionary == NULL) {
00139         return not_enough_ram;
00140     }
00141     // Initialize the first 2^code_len entries of the dictionary with their
00142     // indices.  The rest of the entries will be built up dynamically.
00143 
00144     // Technically, it shouldn't be necessary to initialize the
00145     // dictionary.  The spec says that the encoder "should output a
00146     // clear code as the first code in the image data stream".  It doesn't
00147     // say must, though...
00148     for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
00149         dictionary[dictionary_ind].byte = (uint8_t) dictionary_ind;
00150         // XXX this only works because prev is a 32-bit int (> 12 bits)
00151         dictionary[dictionary_ind].prev = -1;
00152         dictionary[dictionary_ind].len = 1;
00153     }
00154     // 2^code_len + 1 is the special "end" code; don't give it an entry here
00155     dictionary_ind++;
00156     dictionary_ind++;
00157 
00158     // TODO verify that the very last byte is clear_code + 1
00159     while (input_length) {
00160         code = 0x0;
00161         // Always read one more bit than the code length
00162         for (i = 0; i < (code_length + 1); i++) {
00163             // This is different than in the file read example; that
00164             // was a call to "next_bit"
00165             bit = (*input & mask) ? 1 : 0;
00166             mask <<= 1;
00167             if (mask == 0x100) {
00168                 mask = 0x01;
00169                 input++;
00170                 input_length--;
00171             }
00172             code = code | (bit << i);
00173         }
00174         if (code == clear_code) {
00175             code_length = reset_code_length;
00176             dictionary = (dictionary_entry_t *) realloc(dictionary,
00177                 sizeof(dictionary_entry_t) * (1 << (code_length + 1)));
00178             for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
00179                 dictionary[dictionary_ind].byte = (uint8_t) dictionary_ind;
00180                 // XXX this only works because prev is a 32-bit int (> 12 bits)
00181                 dictionary[dictionary_ind].prev = -1;
00182                 dictionary[dictionary_ind].len = 1;
00183             }
00184             dictionary_ind++;
00185             dictionary_ind++;
00186             prev = -1;
00187             continue;
00188         } else if (code == stop_code) {
00189             if (input_length > 1) {
00190                 free(dictionary);
00191                 return not_supported_format;
00192             }
00193             break;
00194         }
00195 
00196         // Update the dictionary with this character plus the _entry_
00197         // (character or string) that came before it
00198         if ((prev > -1) && (code_length < 12)) {
00199             if (code > dictionary_ind) {
00200                 //fprintf(stderr, "code = %.02x, but dictionary_ind = %.02x\n", code, dictionary_ind);
00201                 free(dictionary);
00202                 return not_supported_format;
00203             }
00204             if (code == dictionary_ind) {
00205                 int ptr = prev;
00206 
00207                 while (dictionary[ptr].prev != -1) {
00208                     ptr = dictionary[ptr].prev;
00209                 }
00210                 dictionary[dictionary_ind].byte = dictionary[ptr].byte;
00211             } else {
00212                 int ptr = code;
00213                 while (dictionary[ptr].prev != -1) {
00214                     ptr = dictionary[ptr].prev;
00215                 }
00216                 dictionary[dictionary_ind].byte = dictionary[ptr].byte;
00217             }
00218             dictionary[dictionary_ind].prev = prev;
00219             dictionary[dictionary_ind].len = dictionary[prev].len + 1;
00220             dictionary_ind++;
00221             // GIF89a mandates that this stops at 12 bits
00222             if ((dictionary_ind == (1 << (code_length + 1))) &&
00223                 (code_length < 11)) {
00224                 code_length++;
00225                 dictionary = (dictionary_entry_t *) realloc(dictionary,
00226                     sizeof(dictionary_entry_t) * (1 << (code_length + 1)));
00227             }
00228         }
00229         prev = code;
00230         // Now copy the dictionary entry backwards into "out"
00231         match_len = dictionary[code].len;
00232         while (code != -1) {
00233             int pos = dictionary[code].len - 1;
00234             out[pos] = dictionary[code].byte;
00235             //INFO("%p out[%d] = %02X", out, pos, out[pos]);
00236             if (dictionary[code].prev == code) {
00237                 free(dictionary);
00238                 return not_supported_format;
00239             }
00240             code = dictionary[code].prev;
00241         }
00242         out += match_len;
00243     }
00244     free(dictionary);
00245     return noerror;
00246 }
00247 
00248 
00249 /// read a block
00250 ///
00251 /// @param[in] fh is the handle to the gif file.
00252 /// @param[in] data is a pointer to a pointer to the data being processed.
00253 /// @return -1 on failure, 0 when done, or >0 as the length of a processed data block
00254 ///
00255 int GraphicsDisplay::read_gif_sub_blocks(FILE * fh, unsigned char **data) {
00256     int data_length;
00257     int index;
00258     unsigned char block_size;
00259 
00260     // Everything following are data sub-blocks, until a 0-sized block is encountered.
00261     data_length = 0;
00262     *data = NULL;
00263     index = 0;
00264 
00265     while (1) {
00266         if (read_filesystem_bytes(&block_size, 1, fh) < 1) {
00267             return -1;
00268         }
00269         if (block_size == 0) {
00270             break;
00271         }
00272         data_length += block_size;
00273         *data = (unsigned char *) realloc(*data, data_length);
00274         if (data) {
00275             if (read_filesystem_bytes(*data + index, block_size, fh) < block_size) {
00276                 return -1;
00277             }
00278             index += block_size;
00279         } else {
00280             return -1;
00281         }
00282     }
00283     return data_length;
00284 }
00285 
00286 // color table is encoded as 24-bit values, but we don't need that much space, since
00287 // the RA8875 is configured as either 8 or 16-bit color.
00288 //
00289 RetCode_t GraphicsDisplay::readColorTable(color_t * colorTable, int colorTableSize, FILE * fh) {
00290     struct {
00291         uint8_t r;
00292         uint8_t g;
00293         uint8_t b;
00294     } rgb;
00295     while (colorTableSize--) {
00296         if (read_filesystem_bytes(&rgb, 3, fh) < 3)
00297             return not_supported_format;
00298         *colorTable++ = RGB(rgb.r, rgb.g, rgb.b);
00299     }
00300     return noerror;
00301 }
00302 
00303 RetCode_t GraphicsDisplay::readGIFImageDescriptor(FILE * fh, gif_image_descriptor_t * imageDescriptor) {
00304     if (read_filesystem_bytes(imageDescriptor, gif_image_descriptor_size, fh) < gif_image_descriptor_size)
00305         return not_supported_format;
00306     INFO("gif_image_descriptor");
00307     INFO("       left: %d", imageDescriptor->image_left_position);
00308     INFO("        top: %d", imageDescriptor->image_top_position);
00309     INFO("      width: %d", imageDescriptor->image_width);
00310     INFO("     height: %d", imageDescriptor->image_height);
00311     INFO("     fields: %02Xx", imageDescriptor->fields);
00312     INFO("          lct: %2d", (imageDescriptor->fields & 0x80) ? 1 : 0);
00313     INFO("          res: %2d", (imageDescriptor->fields & 0x40) ? 1 : 0);
00314     INFO("         sort: %2d", (imageDescriptor->fields & 0x20) ? 1 : 0);
00315     INFO("          res: %2d", ((imageDescriptor->fields & 0x18) >> 3));
00316     INFO("locclrtbl siz: %2d", 1 << ((imageDescriptor->fields & 0x07) + 1));
00317     if (imageDescriptor->fields & 0x80) {
00318         local_color_table_size = 1 << ((imageDescriptor->fields & 0x07) + 1);
00319         local_color_table = (color_t *) malloc(sizeof(color_t) * local_color_table_size);
00320         if (local_color_table == NULL)
00321             return not_enough_ram;
00322         if (readColorTable(local_color_table, local_color_table_size, fh) != noerror) {
00323             free(local_color_table);
00324             local_color_table = NULL;
00325             return not_supported_format;
00326         }
00327     }
00328     INFO("gif_image_descriptor end.");
00329     return noerror;
00330 }
00331 
00332 /// Process the image section of the GIF file
00333 ///
00334 /// @param[in] fh is the handle to the file.
00335 /// @param[in] uncompress_gifed_data is a pointer to a pointer to where the data will be placed.
00336 /// @returns true if all went well.
00337 ///
00338 RetCode_t GraphicsDisplay::process_gif_image_descriptor(FILE * fh, uint8_t ** uncompress_gifed_data, int width, int height) {
00339     int compressed_data_length;
00340     unsigned char *compressed_data = NULL;
00341     unsigned char lzw_code_size;
00342     int uncompress_gifed_data_length = 0;
00343     RetCode_t res = not_supported_format;
00344     local_color_table_size = 0;
00345 
00346     if (read_filesystem_bytes(&lzw_code_size, 1, fh) < 1)
00347         goto done;
00348     INFO("lzw_code_size %d", lzw_code_size);
00349     compressed_data_length = read_gif_sub_blocks(fh, &compressed_data);
00350     if (compressed_data_length > 0) {
00351         uncompress_gifed_data_length = width * height;
00352         *uncompress_gifed_data = (unsigned char *)malloc(uncompress_gifed_data_length);
00353         if (*uncompress_gifed_data == NULL) {
00354             return not_enough_ram;
00355         }
00356         res = uncompress_gif(lzw_code_size, compressed_data, compressed_data_length, *uncompress_gifed_data);
00357     }
00358 done:
00359     if (compressed_data)
00360         free(compressed_data);
00361     INFO("lzw_code_size end %d", res);
00362     return res;
00363 }
00364 
00365 
00366 int GraphicsDisplay::process_gif_extension(FILE * fh) {
00367     extension_t extension;
00368     graphic_control_extension_t gce;
00369     application_extension_t application;
00370     plaintext_extension_t plaintext;
00371     unsigned char *extension_data = NULL;
00372     /* int extension_data_length; */
00373 
00374     if (read_filesystem_bytes(&extension, extension_size, fh) < extension_size) {
00375         return 0;
00376     }
00377     INFO("extension");
00378     INFO("        code: %d", extension.extension_code);
00379     INFO("  block size: %d", extension.block_size);
00380     switch (extension.extension_code) {
00381         case GRAPHIC_CONTROL:
00382             if (read_filesystem_bytes(&gce, graphic_control_extension_size, fh) < graphic_control_extension_size) {
00383                 return 0;
00384             }
00385             INFO("graphic_control_extension");
00386             INFO("      fields: %d", gce.fields);
00387             INFO("  delay time: %d", gce.delay_time);
00388             INFO(" transparent: %d", gce.transparent_color_index);
00389             break;
00390         case APPLICATION_EXTENSION:
00391             if (read_filesystem_bytes(&application, application_extension_size, fh) < application_extension_size) {
00392                 return 0;
00393             }
00394             HexDump("application", (const uint8_t *) &application, sizeof(application));
00395             break;
00396         case COMMENT_EXTENSION:
00397             // comment extension; do nothing - all the data is in the
00398             // sub-blocks that follow.
00399             break;
00400         case PLAINTEXT_EXTENSION:
00401             if (read_filesystem_bytes(&plaintext, plaintext_extension_size, fh) < plaintext_extension_size) {
00402                 return 0;
00403             }
00404             HexDump("plaintext", (const uint8_t *) &plaintext, sizeof(plaintext));
00405             break;
00406         default:
00407             return (0);
00408     }
00409     // All extensions are followed by data sub-blocks; even if it's
00410     // just a single data sub-block of length 0
00411     /* extension_data_length = */ read_gif_sub_blocks(fh, &extension_data);
00412     if (extension_data != NULL)
00413         free(extension_data);
00414     return 1;
00415 }
00416 
00417 
00418 RetCode_t GraphicsDisplay::_RenderGIF(loc_t ScreenX, loc_t ScreenY, FILE * fh) {
00419     //int color_resolution_bits;
00420     global_color_table_size = 0;
00421     local_color_table_size = 0;
00422 
00423     if (GetGIFHeader(fh) != noerror)
00424         return not_supported_format;
00425     //color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
00426     if (screen_descriptor.fields & 0x80) {
00427         // If bit 7 is set, the next block is a global color table; read it
00428         global_color_table_size = 1 << ((screen_descriptor.fields & 0x07) + 1);
00429         global_color_table = (color_t *) malloc(sizeof(color_t) * global_color_table_size);
00430         if (global_color_table == NULL)
00431             return not_enough_ram;
00432         // XXX this could conceivably return a short count...
00433         if (readColorTable(global_color_table, global_color_table_size, fh) != noerror) {
00434             return not_supported_format;
00435         }
00436         HexDump("Global Color Table", (const uint8_t *) global_color_table, 3 * global_color_table_size);
00437     }
00438     unsigned char block_type = 0x0;
00439     uint8_t * uncompress_gifed_data = NULL;
00440     gif_image_descriptor_t gif_image_descriptor;        // the image fragment
00441     while (block_type != TRAILER) {
00442         if (read_filesystem_bytes(&block_type, sizeof(block_type), fh) < sizeof(block_type))
00443             return not_supported_format;
00444         INFO("block type: %02X", block_type);
00445        
00446 
00447         RetCode_t r_pgid = noerror;
00448         switch (block_type) {
00449             case IMAGE_DESCRIPTOR:
00450                 if (readGIFImageDescriptor(fh, &gif_image_descriptor) != noerror) {
00451                     INFO("not supported format");
00452                     return not_supported_format;
00453                 }
00454                 r_pgid = process_gif_image_descriptor(fh, &uncompress_gifed_data, gif_image_descriptor.image_width, gif_image_descriptor.image_height);
00455                 if (r_pgid != noerror) {
00456                     INFO("error %d", r_pgid);
00457                     return r_pgid;
00458                 } else {
00459                     if (uncompress_gifed_data) {
00460                         // Ready to render to the screen
00461                         INFO("Render to (%d,%d)", ScreenX, ScreenY);
00462                         color_t * active_color_table = (local_color_table) ? local_color_table : global_color_table;
00463 
00464                         // create a local image buffer for this fragment
00465                         color_t * cbmp = (color_t *) malloc(sizeof(color_t) * gif_image_descriptor.image_width * gif_image_descriptor.image_height);
00466                         if (cbmp == NULL) {
00467                             INFO("not enough ram");
00468                             return not_enough_ram;
00469                         }
00470                         for (int i = 0; i < gif_image_descriptor.image_width * gif_image_descriptor.image_height; i++) {
00471                             int cIndex = uncompress_gifed_data[i];
00472                             cbmp[i] = active_color_table[cIndex];
00473                         }
00474                         // Write Fragment to Screen
00475                         #if 0
00476                         INFO("Render fragment: (%d,%d), offset: (%d,%d), w x h (%d,%d)",
00477                             ScreenX, ScreenY,
00478                             gif_image_descriptor.image_left_position,
00479                             gif_image_descriptor.image_top_position,
00480                             gif_image_descriptor.image_width,
00481                             gif_image_descriptor.image_height);
00482                         for (uint16_t y = 0; y < gif_image_descriptor.image_height; y++) {
00483                             for (uint16_t x = 0; x < gif_image_descriptor.image_width; x++) {
00484                                 INFO("%04X ", cbmp[y * gif_image_descriptor.image_height + x]);
00485                             }
00486                             INFO("");
00487                         }
00488                         #else
00489                         rect_t restore = SetWindow(ScreenX + gif_image_descriptor.image_left_position,
00490                             ScreenY + gif_image_descriptor.image_top_position,
00491                             ScreenX + gif_image_descriptor.image_left_position + gif_image_descriptor.image_width,
00492                             ScreenY + gif_image_descriptor.image_top_position + gif_image_descriptor.image_height);
00493                         pixelStream(cbmp, screen_descriptor.width * screen_descriptor.height,
00494                             ScreenX + gif_image_descriptor.image_left_position,
00495                             ScreenY + gif_image_descriptor.image_top_position);
00496                         SetWindow(restore);
00497                         #endif
00498                         // end write
00499                         free(cbmp);
00500                     }
00501                     if (local_color_table) {
00502                         free(local_color_table);
00503                         local_color_table = NULL;
00504                     }
00505                 }
00506                 break;
00507             case EXTENSION_INTRODUCER:
00508                 if (!process_gif_extension(fh)) {
00509                     INFO("not supported format");
00510                     return not_supported_format;
00511                 }
00512                 break;
00513             case TRAILER:
00514                 break;
00515             default:
00516                 INFO("not supported format");
00517                 return not_supported_format;
00518         }
00519     }
00520     INFO("no error");
00521     return noerror;
00522 }
00523 
00524 
00525 // hasGIFHeader determines if it is a GIF file
00526 //
00527 // This reads a few bytes of the file and determines if they have the
00528 // GIF89a signature. GIF87a is not supported.
00529 //
00530 // @param fh is a file handle.
00531 // @returns true if it is GIF89a.
00532 //
00533 bool GraphicsDisplay::hasGIFHeader(FILE * fh) {
00534     char GIF_Header[6];
00535     if (read_filesystem_bytes(GIF_Header, sizeof(GIF_Header), fh) != sizeof(GIF_Header))
00536         return false;
00537     if (strncmp("GIF89a", GIF_Header, sizeof(GIF_Header)))
00538         return false;
00539     return true;
00540 }
00541 
00542 
00543 RetCode_t GraphicsDisplay::GetGIFHeader(FILE * fh) {
00544     if (read_filesystem_bytes(&screen_descriptor, gif_screen_descriptor_size, fh) < gif_screen_descriptor_size) {
00545         return not_supported_format;
00546     }
00547     screen_descriptor_isvalid = true;
00548     INFO("screen_descriptor");
00549     INFO("      width: %d", screen_descriptor.width);
00550     INFO("     height: %d", screen_descriptor.height);
00551     INFO("     fields: %02Xx", screen_descriptor.fields);
00552     INFO("          gct: %2d", (screen_descriptor.fields & 0x80) ? 1 : 0);
00553     INFO("          res: %2d", ((screen_descriptor.fields & 0x70) >> 4) + 1);
00554     INFO("         sort: %2d", (screen_descriptor.fields & 0x08) ? 1 : 0);
00555     INFO("      gct siz: %2d", 1 << ((screen_descriptor.fields & 0x07) + 1));
00556     INFO("   back clr: %d", screen_descriptor.background_color_index);
00557     INFO("    pix rat: %d", screen_descriptor.pixel_aspect_ratio);
00558     return noerror;
00559 }
00560 
00561 
00562 RetCode_t GraphicsDisplay::GetGIFMetrics(gif_screen_descriptor_t * imageDescriptor, const char * Name_GIF) {
00563     RetCode_t ret = not_supported_format;
00564 
00565     if (screen_descriptor_isvalid) {
00566         ret = noerror;
00567     } else {
00568         FILE *fh = fopen(Name_GIF, "rb");
00569         if (fh) {
00570             ret = GetGIFHeader(fh);
00571             fclose(fh);
00572         }
00573     }
00574     if (ret == noerror)
00575         *imageDescriptor = screen_descriptor;
00576     return ret;
00577 }
00578 
00579 
00580 RetCode_t GraphicsDisplay::RenderGIFFile(loc_t x, loc_t y, const char *Name_GIF) {
00581     RetCode_t rt = file_not_found;
00582 
00583     INFO("Opening {%s}", Name_GIF);
00584     screen_descriptor_isvalid = false;
00585     FILE *fh = fopen(Name_GIF, "rb");
00586     if (fh) {
00587         if (hasGIFHeader(fh)) {
00588             rt = _RenderGIF(x, y, fh);
00589         }
00590         fclose(fh);
00591         if (global_color_table)
00592             free(global_color_table);
00593         if (local_color_table)
00594             free(local_color_table);
00595     }
00596     return rt;
00597 }
00598 
00599