00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #ifdef HAVE_CONFIG_H
00017 # include "config.h"
00018 #elif !defined(__cplusplus) && !defined(inline)
00019
00020 # define inline
00021 #endif
00022 #include "gif.h"
00023 #include <stdarg.h>
00024 #include <string.h>
00025 #ifdef __cplusplus
00026 extern "C" {
00027 #endif
00028
00029 #define WRITE_BUFFER_SIZE 255
00030 #define NODES_SIZE GIF_MAX_CODE
00031 #define LINKS_SIZE GIF_MAX_CODE
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052 #define TABLE_TYPE 0
00053 #define LINKS_TYPE 1
00054 #define MAX_LINKS_TYPE 5
00055 typedef struct Gif_Node {
00056
00057 Gif_Code code;
00058 byte type;
00059 byte suffix;
00060 struct Gif_Node *sibling;
00061 union {
00062 struct Gif_Node *s;
00063 struct Gif_Node **m;
00064 } child;
00065
00066 } Gif_Node;
00067
00068
00069 typedef struct Gif_Context {
00070
00071 Gif_Node *nodes;
00072 int nodes_pos;
00073 Gif_Node **links;
00074 int links_pos;
00075
00076 } Gif_Context;
00077
00078
00079 typedef struct Gif_Writer {
00080
00081 FILE *f;
00082 byte *v;
00083 u_int32_t pos;
00084 u_int32_t cap;
00085 int flags;
00086 int global_size;
00087 int local_size;
00088 void (*byte_putter)(byte, struct Gif_Writer *);
00089 void (*block_putter)(byte *, u_int16_t, struct Gif_Writer *);
00090
00091 } Gif_Writer;
00092
00093
00094 #define gifputbyte(b, grr) ((*grr->byte_putter)(b, grr))
00095 #define gifputblock(b, l, grr) ((*grr->block_putter)(b, l, grr))
00096
00097 static inline void
00098 gifputunsigned(u_int16_t uns, Gif_Writer *grr)
00099 {
00100 gifputbyte(uns & 0xFF, grr);
00101 gifputbyte(uns >> 8, grr);
00102 }
00103
00104
00105 static void
00106 file_byte_putter(byte b, Gif_Writer *grr)
00107 {
00108 fputc(b, grr->f);
00109 }
00110
00111 static void
00112 file_block_putter(byte *block, u_int16_t size, Gif_Writer *grr)
00113 {
00114 fwrite(block, size, 1, grr->f);
00115 }
00116
00117
00118 static void
00119 memory_byte_putter(byte b, Gif_Writer *grr)
00120 {
00121 if (grr->pos >= grr->cap) {
00122 grr->cap *= 2;
00123 Gif_ReArray(grr->v, byte, grr->cap);
00124 }
00125 if (grr->v) {
00126 grr->v[grr->pos] = b;
00127 grr->pos++;
00128 }
00129 }
00130
00131 static void
00132 memory_block_putter(byte *data, u_int16_t len, Gif_Writer *grr)
00133 {
00134 if (grr->pos + len >= grr->cap) {
00135 grr->cap *= 2;
00136 Gif_ReArray(grr->v, byte, grr->cap);
00137 }
00138 if (grr->v) {
00139 memcpy(grr->v + grr->pos, data, len);
00140 grr->pos += len;
00141 }
00142 }
00143
00144
00145 static void
00146 change_node_to_table(Gif_Context *gfc, Gif_Node *work_node,
00147 Gif_Node *next_node, Gif_Code clear_code)
00148 {
00149
00150 Gif_Code c;
00151 Gif_Node **table = &gfc->links[gfc->links_pos];
00152 Gif_Node *n;
00153 gfc->links_pos += clear_code;
00154
00155 for (c = 0; c < clear_code; c++)
00156 table[c] = 0;
00157 table[next_node->suffix] = next_node;
00158 for (n = work_node->child.s; n; n = n->sibling)
00159 table[n->suffix] = n;
00160
00161 work_node->type = TABLE_TYPE;
00162 work_node->child.m = table;
00163 }
00164
00165
00166 static void
00167 write_compressed_data(byte **img, u_int16_t width, u_int16_t height,
00168 byte min_code_bits, Gif_Context *gfc, Gif_Writer *grr)
00169 {
00170 byte buffer[WRITE_BUFFER_SIZE];
00171 byte *buf;
00172
00173 u_int16_t xleft;
00174 byte *imageline;
00175
00176 u_int32_t leftover;
00177 byte bits_left_over;
00178
00179 Gif_Node *work_node;
00180 Gif_Node *next_node;
00181 Gif_Code next_code;
00182 Gif_Code output_code;
00183 Gif_Code clear_code;
00184 Gif_Code eoi_code;
00185 #define CUR_BUMP_CODE (1 << cur_code_bits)
00186 byte suffix;
00187
00188 byte cur_code_bits;
00189
00190
00191 gifputbyte(min_code_bits, grr);
00192 clear_code = 1 << min_code_bits;
00193 eoi_code = clear_code + 1;
00194
00195 cur_code_bits = min_code_bits + 1;
00196
00197 GIF_DEBUG(("clear(%d) eoi(%d) bits(%d)",clear_code,eoi_code,cur_code_bits));
00198
00199 work_node = 0;
00200 output_code = clear_code;
00201
00202
00203
00204 bits_left_over = 0;
00205 leftover = 0;
00206 buf = buffer;
00207 xleft = width;
00208 imageline = img[0];
00209
00210 while (1) {
00211
00212
00213
00214
00215 leftover |= output_code << bits_left_over;
00216 bits_left_over += cur_code_bits;
00217 while (bits_left_over >= 8) {
00218 *buf++ = leftover & 0xFF;
00219 leftover = (leftover >> 8) & 0x00FFFFFF;
00220 bits_left_over -= 8;
00221 if (buf == buffer + WRITE_BUFFER_SIZE) {
00222 gifputbyte(WRITE_BUFFER_SIZE, grr);
00223 gifputblock(buffer, WRITE_BUFFER_SIZE, grr);
00224 buf = buffer;
00225 }
00226 }
00227
00228 if (output_code == clear_code) {
00229
00230 Gif_Code c;
00231
00232 cur_code_bits = min_code_bits + 1;
00233 next_code = eoi_code + 1;
00234
00235
00236 gfc->nodes_pos = clear_code;
00237 gfc->links_pos = 0;
00238 for (c = 0; c < clear_code; c++) {
00239 gfc->nodes[c].code = c;
00240 gfc->nodes[c].type = LINKS_TYPE;
00241 gfc->nodes[c].suffix = c;
00242 gfc->nodes[c].child.s = 0;
00243 }
00244
00245 } else if (next_code > CUR_BUMP_CODE) {
00246
00247 if (cur_code_bits == GIF_MAX_CODE_BITS) {
00248 output_code = clear_code;
00249 continue;
00250 } else
00251 cur_code_bits++;
00252
00253 } else if (output_code == eoi_code)
00254 break;
00255
00256
00257
00258
00259
00260
00261
00262 while (height != 0) {
00263 suffix = *imageline;
00264 if (suffix >= clear_code)
00265
00266 suffix = 0;
00267 if (!work_node)
00268 next_node = &gfc->nodes[suffix];
00269 else if (work_node->type == TABLE_TYPE)
00270 next_node = work_node->child.m[suffix];
00271 else
00272 for (next_node = work_node->child.s; next_node;
00273 next_node = next_node->sibling)
00274 if (next_node->suffix == suffix)
00275 break;
00276
00277 imageline++;
00278 xleft--;
00279 if (xleft == 0) {
00280 xleft = width;
00281 height--;
00282 img++;
00283 imageline = img[0];
00284 }
00285
00286 if (!next_node) {
00287
00288
00289
00290 next_node = &gfc->nodes[gfc->nodes_pos];
00291 gfc->nodes_pos++;
00292 next_node->code = next_code;
00293 next_code++;
00294 next_node->type = LINKS_TYPE;
00295 next_node->suffix = suffix;
00296 next_node->child.s = 0;
00297
00298
00299 if (work_node->type == TABLE_TYPE)
00300 work_node->child.m[suffix] = next_node;
00301 else if (work_node->type < MAX_LINKS_TYPE
00302 || gfc->links_pos + clear_code > LINKS_SIZE) {
00303 next_node->sibling = work_node->child.s;
00304 work_node->child.s = next_node;
00305 work_node->type++;
00306 } else
00307 change_node_to_table(gfc, work_node, next_node, clear_code);
00308
00309
00310 output_code = work_node->code;
00311 work_node = &gfc->nodes[suffix];
00312 goto found_output_code;
00313 }
00314
00315 work_node = next_node;
00316 }
00317
00318
00319 output_code = (work_node ? work_node->code : eoi_code);
00320 work_node = 0;
00321
00322 found_output_code: ;
00323 }
00324
00325 if (bits_left_over > 0)
00326 *buf++ = leftover;
00327
00328 if (buf != buffer) {
00329 GIF_DEBUG(("imageblock(%d)", buf - buffer));
00330 gifputbyte(buf - buffer, grr);
00331 gifputblock(buffer, buf - buffer, grr);
00332 }
00333
00334 gifputbyte(0, grr);
00335 }
00336
00337
00338 static int
00339 calculate_min_code_bits(Gif_Stream *gfs, Gif_Image *gfi, Gif_Writer *grr)
00340 {
00341 int colors_used = -1, min_code_bits, i;
00342
00343 if (grr->flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE) {
00344
00345 if (grr->local_size > 0)
00346 colors_used = grr->local_size;
00347 else if (grr->global_size > 0)
00348 colors_used = grr->global_size;
00349
00350 } else if (gfi->compressed) {
00351
00352 colors_used = 1 << gfi->compressed[0];
00353
00354 } else if (gfi->img) {
00355
00356 int x, y, width = gfi->width, height = gfi->height;
00357 colors_used = 0;
00358 for (y = 0; y < height && colors_used < 128; y++) {
00359 byte *data = gfi->img[y];
00360 for (x = width; x > 0; x--, data++)
00361 if (*data > colors_used)
00362 colors_used = *data;
00363 }
00364 colors_used++;
00365
00366 } else {
00367
00368 colors_used = 256;
00369 }
00370
00371 min_code_bits = 2;
00372 i = 4;
00373 while (i < colors_used) {
00374 min_code_bits++;
00375 i *= 2;
00376 }
00377
00378 if ((grr->flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE)
00379 && gfi->compressed && gfi->compressed[0] != min_code_bits) {
00380
00381 if (Gif_UncompressImage(gfi))
00382 Gif_FullCompressImage(gfs, gfi, grr->flags);
00383 }
00384
00385 return min_code_bits;
00386 }
00387
00388 static int
00389 write_image_data(Gif_Image *gfi, byte min_code_bits,
00390 Gif_Context *gfc, Gif_Writer *grr)
00391 {
00392 byte **img = gfi->img;
00393 u_int16_t width = gfi->width, height = gfi->height;
00394
00395 if (gfi->interlace) {
00396 u_int16_t y;
00397 byte **nimg = Gif_NewArray(byte *, height + 1);
00398 if (!nimg) return 0;
00399
00400 for (y = 0; y < height; y++)
00401 nimg[y] = img[Gif_InterlaceLine(y, height)];
00402 nimg[height] = 0;
00403
00404 write_compressed_data(nimg, width, height, min_code_bits, gfc, grr);
00405
00406 Gif_DeleteArray(nimg);
00407 } else
00408 write_compressed_data(img, width, height, min_code_bits, gfc, grr);
00409
00410 return 1;
00411 }
00412
00413
00414 static int get_color_table_size(Gif_Stream *, Gif_Image *, Gif_Writer *);
00415
00416 int
00417 Gif_FullCompressImage(Gif_Stream *gfs, Gif_Image *gfi, int flags)
00418 {
00419 int ok = 0;
00420 byte min_code_bits;
00421 Gif_Writer grr;
00422 Gif_Context gfc;
00423
00424 if (gfi->compressed && gfi->free_compressed) {
00425 (*gfi->free_compressed)((void *)gfi->compressed);
00426 gfi->compressed = 0;
00427 }
00428
00429 gfc.nodes = Gif_NewArray(Gif_Node, NODES_SIZE);
00430 gfc.links = Gif_NewArray(Gif_Node *, LINKS_SIZE);
00431
00432 grr.v = Gif_NewArray(byte, 1024);
00433 grr.pos = 0;
00434 grr.cap = 1024;
00435 grr.byte_putter = memory_byte_putter;
00436 grr.block_putter = memory_block_putter;
00437 grr.flags = flags;
00438 grr.global_size = get_color_table_size(gfs, 0, &grr);
00439 grr.local_size = get_color_table_size(gfs, gfi, &grr);
00440
00441 if (!gfc.nodes || !gfc.links || !grr.v)
00442 goto done;
00443
00444 min_code_bits = calculate_min_code_bits(gfs, gfi, &grr);
00445 ok = write_image_data(gfi, min_code_bits, &gfc, &grr);
00446
00447 done:
00448 if (!ok) {
00449 Gif_DeleteArray(grr.v);
00450 grr.v = 0;
00451 }
00452 gfi->compressed = grr.v;
00453 gfi->compressed_len = grr.pos;
00454 gfi->free_compressed = Gif_DeleteArrayFunc;
00455 Gif_DeleteArray(gfc.nodes);
00456 Gif_DeleteArray(gfc.links);
00457 return grr.v != 0;
00458 }
00459
00460
00461 static int
00462 get_color_table_size(Gif_Stream *gfs, Gif_Image *gfi, Gif_Writer *grr)
00463 {
00464 Gif_Colormap *gfcm = (gfi ? gfi->local : gfs->global);
00465 int ncol, totalcol, i;
00466
00467 if (!gfcm || gfcm->ncol <= 0)
00468 return 0;
00469
00470
00471 ncol = gfcm->ncol;
00472
00473
00474
00475 if (grr->flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE) {
00476 if (gfi && gfi->transparent >= ncol)
00477 ncol = gfi->transparent + 1;
00478 else if (!gfi)
00479 for (i = 0; i < gfs->nimages; i++)
00480 if (gfs->images[i]->transparent >= ncol)
00481 ncol = gfs->images[i]->transparent + 1;
00482 }
00483
00484
00485
00486 if (ncol > 256)
00487 ncol = 256;
00488 for (totalcol = 2; totalcol < ncol; totalcol *= 2)
00489 ;
00490
00491 return totalcol;
00492 }
00493
00494 static void
00495 write_color_table(Gif_Colormap *gfcm, int totalcol, Gif_Writer *grr)
00496 {
00497 Gif_Color *c = gfcm->col;
00498 int i, ncol = gfcm->ncol;
00499
00500 for (i = 0; i < ncol && i < totalcol; i++, c++) {
00501 gifputbyte(c->red, grr);
00502 gifputbyte(c->green, grr);
00503 gifputbyte(c->blue, grr);
00504 }
00505
00506
00507 for (; i < totalcol; i++) {
00508 gifputbyte(0, grr);
00509 gifputbyte(0, grr);
00510 gifputbyte(0, grr);
00511 }
00512 }
00513
00514
00515 static int
00516 write_image(Gif_Stream *gfs, Gif_Image *gfi, Gif_Context *gfc, Gif_Writer *grr)
00517 {
00518 byte min_code_bits, packed = 0;
00519 grr->local_size = get_color_table_size(gfs, gfi, grr);
00520
00521 gifputbyte(',', grr);
00522 gifputunsigned(gfi->left, grr);
00523 gifputunsigned(gfi->top, grr);
00524 gifputunsigned(gfi->width, grr);
00525 gifputunsigned(gfi->height, grr);
00526
00527 if (grr->local_size > 0) {
00528 int size = 2;
00529 packed |= 0x80;
00530 while (size < grr->local_size)
00531 size *= 2, packed++;
00532 }
00533
00534 if (gfi->interlace) packed |= 0x40;
00535 gifputbyte(packed, grr);
00536
00537 if (grr->local_size > 0)
00538 write_color_table(gfi->local, grr->local_size, grr);
00539
00540
00541
00542 min_code_bits = calculate_min_code_bits(gfs, gfi, grr);
00543
00544
00545
00546
00547 if (gfi->compressed) {
00548 byte *compressed = gfi->compressed;
00549 u_int32_t compressed_len = gfi->compressed_len;
00550 while (compressed_len > 0) {
00551 u_int16_t amt = (compressed_len > 0x7000 ? 0x7000 : compressed_len);
00552 gifputblock(compressed, amt, grr);
00553 compressed += amt;
00554 compressed_len -= amt;
00555 }
00556
00557 } else
00558 write_image_data(gfi, min_code_bits, gfc, grr);
00559
00560 return 1;
00561 }
00562
00563
00564 static void
00565 write_logical_screen_descriptor(Gif_Stream *gfs, Gif_Writer *grr)
00566 {
00567 byte packed = 0x70;
00568 grr->global_size = get_color_table_size(gfs, 0, grr);
00569
00570 Gif_CalculateScreenSize(gfs, 0);
00571 gifputunsigned(gfs->screen_width, grr);
00572 gifputunsigned(gfs->screen_height, grr);
00573
00574 if (grr->global_size > 0) {
00575 u_int16_t size = 2;
00576 packed |= 0x80;
00577 while (size < grr->global_size && size < 256)
00578 size *= 2, packed++;
00579 }
00580
00581 gifputbyte(packed, grr);
00582 gifputbyte(gfs->background, grr);
00583 gifputbyte(0, grr);
00584
00585 if (grr->global_size > 0)
00586 write_color_table(gfs->global, grr->global_size, grr);
00587 }
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598 static void
00599 write_graphic_control_extension(Gif_Image *gfi, Gif_Writer *grr)
00600 {
00601 byte packed = 0;
00602 gifputbyte('!', grr);
00603 gifputbyte(0xF9, grr);
00604 gifputbyte(4, grr);
00605 if (gfi->transparent >= 0) packed |= 0x01;
00606 packed |= (gfi->disposal & 0x07) << 2;
00607 gifputbyte(packed, grr);
00608 gifputunsigned(gfi->delay, grr);
00609 gifputbyte((byte)gfi->transparent, grr);
00610 gifputbyte(0, grr);
00611 }
00612
00613
00614 static void
00615 blast_data(byte *data, int len, Gif_Writer *grr)
00616 {
00617 while (len > 0) {
00618 int s = len > 255 ? 255 : len;
00619 gifputbyte(s, grr);
00620 gifputblock(data, s, grr);
00621 data += s;
00622 len -= s;
00623 }
00624 gifputbyte(0, grr);
00625 }
00626
00627
00628 static void
00629 write_name_extension(char *id, Gif_Writer *grr)
00630 {
00631 gifputbyte('!', grr);
00632 gifputbyte(0xCE, grr);
00633 blast_data((byte *)id, strlen(id), grr);
00634 }
00635
00636
00637 static void
00638 write_comment_extensions(Gif_Comment *gfcom, Gif_Writer *grr)
00639 {
00640 int i;
00641 for (i = 0; i < gfcom->count; i++) {
00642 gifputbyte('!', grr);
00643 gifputbyte(0xFE, grr);
00644 blast_data((byte *)gfcom->str[i], gfcom->len[i], grr);
00645 }
00646 }
00647
00648
00649 static void
00650 write_netscape_loop_extension(u_int16_t value, Gif_Writer *grr)
00651 {
00652 gifputblock((byte *)"!\xFF\x0BNETSCAPE2.0\x03\x01", 16, grr);
00653 gifputunsigned(value, grr);
00654 gifputbyte(0, grr);
00655 }
00656
00657
00658 static void
00659 write_generic_extension(Gif_Extension *gfex, Gif_Writer *grr)
00660 {
00661 u_int32_t pos = 0;
00662 if (gfex->kind < 0) return;
00663
00664 gifputbyte('!', grr);
00665 gifputbyte(gfex->kind, grr);
00666 if (gfex->kind == 255) {
00667 int len = gfex->application ? strlen(gfex->application) : 0;
00668 if (len) {
00669 gifputbyte(len, grr);
00670 gifputblock((byte *)gfex->application, len, grr);
00671 }
00672 }
00673 while (pos + 255 < gfex->length) {
00674 gifputbyte(255, grr);
00675 gifputblock(gfex->data + pos, 255, grr);
00676 pos += 255;
00677 }
00678 if (pos < gfex->length) {
00679 u_int32_t len = gfex->length - pos;
00680 gifputbyte(len, grr);
00681 gifputblock(gfex->data + pos, len, grr);
00682 }
00683 gifputbyte(0, grr);
00684 }
00685
00686
00687 static int
00688 write_gif(Gif_Stream *gfs, Gif_Writer *grr)
00689 {
00690 int ok = 0;
00691 int i;
00692 Gif_Image *gfi;
00693 Gif_Extension *gfex = gfs->extensions;
00694 Gif_Context gfc;
00695
00696 gfc.nodes = Gif_NewArray(Gif_Node, NODES_SIZE);
00697 gfc.links = Gif_NewArray(Gif_Node *, LINKS_SIZE);
00698 if (!gfc.nodes || !gfc.links)
00699 goto done;
00700
00701 {
00702 byte isgif89a = 0;
00703 if (gfs->comment || gfs->loopcount > -1)
00704 isgif89a = 1;
00705 for (i = 0; i < gfs->nimages && !isgif89a; i++) {
00706 gfi = gfs->images[i];
00707 if (gfi->identifier || gfi->transparent != -1 || gfi->disposal ||
00708 gfi->delay || gfi->comment)
00709 isgif89a = 1;
00710 }
00711 if (isgif89a)
00712 gifputblock((byte *)"GIF89a", 6, grr);
00713 else
00714 gifputblock((byte *)"GIF87a", 6, grr);
00715 }
00716
00717 write_logical_screen_descriptor(gfs, grr);
00718
00719 if (gfs->loopcount > -1)
00720 write_netscape_loop_extension(gfs->loopcount, grr);
00721
00722 for (i = 0; i < gfs->nimages; i++) {
00723 Gif_Image *gfi = gfs->images[i];
00724 while (gfex && gfex->position == i) {
00725 write_generic_extension(gfex, grr);
00726 gfex = gfex->next;
00727 }
00728 if (gfi->comment)
00729 write_comment_extensions(gfi->comment, grr);
00730 if (gfi->identifier)
00731 write_name_extension(gfi->identifier, grr);
00732 if (gfi->transparent != -1 || gfi->disposal || gfi->delay)
00733 write_graphic_control_extension(gfi, grr);
00734 if (!write_image(gfs, gfi, &gfc, grr))
00735 goto done;
00736 }
00737
00738 while (gfex) {
00739 write_generic_extension(gfex, grr);
00740 gfex = gfex->next;
00741 }
00742 if (gfs->comment)
00743 write_comment_extensions(gfs->comment, grr);
00744
00745 gifputbyte(';', grr);
00746 ok = 1;
00747
00748 done:
00749 Gif_DeleteArray(gfc.nodes);
00750 Gif_DeleteArray(gfc.links);
00751 return ok;
00752 }
00753
00754
00755 int
00756 Gif_FullWriteFile(Gif_Stream *gfs, int flags, FILE *f)
00757 {
00758 Gif_Writer grr;
00759 grr.f = f;
00760 grr.byte_putter = file_byte_putter;
00761 grr.block_putter = file_block_putter;
00762 grr.flags = flags;
00763 return write_gif(gfs, &grr);
00764 }
00765
00766
00767 #undef Gif_CompressImage
00768 #undef Gif_WriteFile
00769
00770 int
00771 Gif_CompressImage(Gif_Stream *gfs, Gif_Image *gfi)
00772 {
00773 return Gif_FullCompressImage(gfs, gfi, 0);
00774 }
00775
00776 int
00777 Gif_WriteFile(Gif_Stream *gfs, FILE *f)
00778 {
00779 return Gif_FullWriteFile(gfs, 0, f);
00780 }
00781
00782
00783 #ifdef __cplusplus
00784 }
00785 #endif