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