KSM edits to RA8875
Embed:
(wiki syntax)
Show/hide line numbers
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 Tue Jul 26 2022 07:46:24 by 1.7.2