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