7 struct gif_scalar_info {
14 my_gif_inputfunc(GifFileType* gft, GifByteType *buf,int length) {
15 struct gif_scalar_info *gsi=(struct gif_scalar_info *)gft->UserData;
16 /* fprintf(stderr,"my_gif_inputfunc: length=%d cpos=%d tlength=%d\n",length,gsi->cpos,gsi->length); */
18 if (gsi->cpos == gsi->length) return 0;
19 if (gsi->cpos+length > gsi->length) length=gsi->length-gsi->cpos; /* Don't read too much */
20 memcpy(buf,gsi->data+gsi->cpos,length);
28 This file needs a complete rewrite
30 This file needs a complete rewrite
32 Maybe not anymore, though reading still needs to support reading
33 all those gif properties.
36 /* Make some variables global, so we could access them faster: */
42 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
43 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
44 static ColorMapObject *ColorMap;
47 i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
49 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
50 GifRecordType RecordType;
51 GifByteType *Extension;
54 static GifColorType *ColorMapEntry;
57 /* unsigned char *Buffer, *BufferP; */
59 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
61 BackGround = GifFile->SBackGroundColor;
62 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
63 ColorMapSize = ColorMap->ColorCount;
65 /* **************************************** */
66 if(colour_table != NULL) {
68 *colour_table=mymalloc(sizeof(int *) * ColorMapSize * 3);
69 if(*colour_table == NULL)
70 m_fatal(0,"Failed to allocate memory for GIF colour table, aborted.");
72 memset(*colour_table, 0, sizeof(int *) * ColorMapSize * 3);
74 for(q=0; q<ColorMapSize; q++) {
75 ColorMapEntry = &ColorMap->Colors[q];
76 (*colour_table)[q*3 + 0]=ColorMapEntry->Red;
77 (*colour_table)[q*3 + 1]=ColorMapEntry->Green;
78 (*colour_table)[q*3 + 2]=ColorMapEntry->Blue;
83 *colours = ColorMapSize;
86 /* **************************************** */
87 im=i_img_empty_ch(NULL,GifFile->SWidth,GifFile->SHeight,3);
89 Size = GifFile->SWidth * sizeof(GifPixelType);
91 if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
92 m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
94 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
96 /* Scan the content of the GIF file and load the image(s) in: */
98 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
103 switch (RecordType) {
104 case IMAGE_DESC_RECORD_TYPE:
105 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
109 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
110 Col = GifFile->Image.Left;
111 Width = GifFile->Image.Width;
112 Height = GifFile->Image.Height;
114 mm_log((1,"i_readgif: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
116 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
117 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
118 fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
121 if (GifFile->Image.Interlace) {
123 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
125 if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
130 for (x = 0; x < GifFile->SWidth; x++) {
131 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
132 col.rgb.r = ColorMapEntry->Red;
133 col.rgb.g = ColorMapEntry->Green;
134 col.rgb.b = ColorMapEntry->Blue;
141 for (i = 0; i < Height; i++) {
142 if (DGifGetLine(GifFile, &GifRow[Col], Width) == GIF_ERROR) {
143 mm_log((1,"fatal\n"));
147 for (x = 0; x < GifFile->SWidth; x++) {
148 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
149 col.rgb.r = ColorMapEntry->Red;
150 col.rgb.g = ColorMapEntry->Green;
151 col.rgb.b = ColorMapEntry->Blue;
152 i_ppix(im,x,Row,&col);
158 case EXTENSION_RECORD_TYPE:
159 /* Skip any extension blocks in file: */
160 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
161 mm_log((1,"fatal\n"));
164 while (Extension != NULL) {
165 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
166 mm_log((1,"fatal\n"));
171 case TERMINATE_RECORD_TYPE:
173 default: /* Should be traps by DGifGetRecordType. */
176 } while (RecordType != TERMINATE_RECORD_TYPE);
180 if (DGifCloseFile(GifFile) == GIF_ERROR) {
189 i_readgif(int fd, int **colour_table, int *colours) {
190 GifFileType *GifFile;
192 mm_log((1,"i_readgif(fd %d, colour_table %p, colours %p)\n", fd, colour_table, colours));
194 if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
195 mm_log((1,"i_readgif: Unable to open file\n"));
199 return i_readgif_low(GifFile, colour_table, colours);
204 i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
210 memset(&quant, 0, sizeof(quant));
211 memset(&opts, 0, sizeof(opts));
212 quant.make_colors = mc_addi;
213 quant.mc_colors = colors;
214 quant.mc_size = 1<<max_colors;
215 quant.mc_count = fixedlen;
216 memcpy(colors, fixed, fixedlen * sizeof(i_color));
217 quant.translate = pt_perturb;
218 quant.perturb = pixdev;
219 return i_writegif_gen(&quant, fd, &im, 1, &opts);
223 i_writegifmc(i_img *im, int fd, int max_colors) {
228 memset(&quant, 0, sizeof(quant));
229 memset(&opts, 0, sizeof(opts));
230 quant.make_colors = mc_none; /* ignored for pt_giflib */
231 quant.mc_colors = colors;
232 quant.mc_size = 1 << max_colors;
234 quant.translate = pt_giflib;
235 return i_writegif_gen(&quant, fd, &im, 1, &opts);
241 i_writegifex(i_img *im, int fd) {
247 /* I don't think this works
248 note that RedBuffer is never allocated - TC
252 i_writegifex(i_img *im,int fd) {
253 int colors, xsize, ysize, channels;
254 int x,y,ColorMapSize;
259 GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL,*OutputBuffer = NULL;
260 ColorMapObject *OutputColorMap = NULL;
261 GifFileType *GifFile;
266 mm_log((1,"i_writegif(0x%x,fd %d)\n",im,fd));
268 if (!(im->channels==1 || im->channels==3)) { fprintf(stderr,"Unable to write gif, improper colorspace.\n"); exit(3); }
272 channels=im->channels;
278 for(x=0;x<xsize;x++) for(y=0;y<ysize;y++) {
280 colors+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
281 /* if (colors > maxc) { octt_delete(ct); }
282 We'll just bite the bullet */
285 ColorMapSize = (colors > 256) ? 256 : colors;
287 Size = ((long) im->xsize) * im->ysize * sizeof(GifByteType);
289 if ((OutputColorMap = MakeMapObject(ColorMapSize, NULL)) == NULL)
290 m_fatal(0,"Failed to allocate memory for Output colormap.");
291 if ((OutputBuffer = (GifByteType *) mymalloc(im->xsize * im->ysize * sizeof(GifByteType))) == NULL)
292 m_fatal(0,"Failed to allocate memory for output buffer.");
294 if (QuantizeBuffer(im->xsize, im->ysize, &ColorMapSize, RedBuffer, GreenBuffer, BlueBuffer,
295 OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) {
296 mm_log((1,"Error in QuantizeBuffer, unable to write image.\n"));
302 if (im->channels == 3) { myfree(GreenBuffer); myfree(BlueBuffer); }
304 if ((GifFile = EGifOpenFileHandle(fd)) == NULL) {
305 mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
309 if (EGifPutScreenDesc(GifFile,im->xsize, im->ysize, colors, 0,OutputColorMap) == GIF_ERROR ||
310 EGifPutImageDesc(GifFile,0, 0, im->xsize, im->ysize, FALSE, NULL) == GIF_ERROR) {
311 mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
312 if (GifFile != NULL) EGifCloseFile(GifFile);
318 for (y = 0; y < im->ysize; y++) {
319 if (EGifPutLine(GifFile, Ptr, im->xsize) == GIF_ERROR) {
320 mm_log((1,"Error in EGifOpenFileHandle, unable to write image.\n"));
321 if (GifFile != NULL) EGifCloseFile(GifFile);
328 if (EGifCloseFile(GifFile) == GIF_ERROR) {
329 mm_log((1,"Error in EGifCloseFile, unable to write image.\n"));
338 i_readgif_scalar(char *data, int length, int **colour_table, int *colours) {
340 GifFileType *GifFile;
342 struct gif_scalar_info gsi;
348 mm_log((1,"i_readgif_scalar(char* data, int length, colour_table %p, colours %p)\n", data, length, colour_table, colours));
349 if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
350 mm_log((1,"i_readgif_scalar: Unable to open scalar datasource.\n"));
354 return i_readgif_low(GifFile, colour_table, colours);
363 gif_read_callback(GifFileType *gft, GifByteType *buf, int length) {
364 return i_gen_reader((i_gen_read_data *)gft->UserData, buf, length);
370 i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours) {
372 GifFileType *GifFile;
375 i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
377 mm_log((1,"i_readgif_callback(callback %p, userdata %p, colour_table %p, colours %p)\n", cb, userdata, colour_table, colours));
378 if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
379 mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
384 result = i_readgif_low(GifFile, colour_table, colours);
385 free_gen_read_data(gci);
393 /* low level image write
394 writes in interlace if that's what was requested */
396 do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data) {
397 if (opts->interlace) {
399 for (i = 0; i < 4; ++i) {
400 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
401 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
402 mm_log((1, "Error in EGifPutLine\n"));
411 for (y = 0; y < img->ysize; ++y) {
412 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
413 mm_log((1, "Error in EGifPutLine\n"));
424 static int do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
426 unsigned char gce[4] = {0};
430 gce[3] = trans_index;
433 if (index < opts->delay_count) {
434 gce[1] = opts->delays[index] % 256;
435 gce[2] = opts->delays[index] / 256;
438 if (index < opts->user_input_count) {
439 if (opts->user_input_flags[index])
443 if (index < opts->disposal_count) {
444 gce[0] |= (opts->disposal[index] & 3) << 2;
448 return EGifPutExtension(gf, 0xF9, sizeof(gce), gce) != GIF_ERROR;
453 /* add the Netscape2.0 loop extension block, if requested */
454 static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
457 /* EGifPutExtension() doesn't appear to handle application
458 extension blocks in any way
459 Since giflib wraps the fd with a FILE * (and puts that in its
460 private data), we can't do an end-run and write the data
462 There's no open interface that takes a FILE * either, so we
463 can't workaround it that way either.
464 If giflib's callback interface wasn't broken by default, I'd
465 force file writes to use callbacks, but it is broken by default.
467 if (opts->loop_count) {
468 unsigned char nsle[15] = "NETSCAPE2.0";
471 nsle[13] = opts->loop_count % 256;
472 nsle[14] = opts->loop_count / 256;
473 return EGifPutExtension(gf, 0xFF, sizeof(nsle), nsle) != GIF_ERROR;
479 static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
481 GifColorType colors[256];
483 int size = quant->mc_count;
486 for (i = 0; i < quant->mc_count; ++i) {
487 colors[i].Red = quant->mc_colors[i].rgb.r;
488 colors[i].Green = quant->mc_colors[i].rgb.g;
489 colors[i].Blue = quant->mc_colors[i].rgb.b;
492 colors[size].Red = opts->tran_color.rgb.r;
493 colors[size].Green = opts->tran_color.rgb.g;
494 colors[size].Blue = opts->tran_color.rgb.b;
498 while (map_size < size)
500 return MakeMapObject(map_size, colors);
503 /* we need to call EGifSetGifVersion() before opening the file - put that
506 static void gif_set_version(i_quantize *quant, i_gif_opts *opts) {
507 /* the following crashed giflib
508 the EGifSetGifVersion() is seriously borked in giflib
509 it's less borked in the ungiflib beta, but we don't have a mechanism
511 if (opts->delay_count
512 || opts->user_input_count
513 || opts->disposal_count
515 || quant->transp != tr_none)
516 EGifSetGifVersion("89a");
518 EGifSetGifVersion("87a");
523 i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
525 unsigned char *result;
528 int scrw = 0, scrh = 0;
529 int imgn, orig_count, orig_size;
532 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d, opts %p)\n",
533 quant, gf, imgs, count, opts));
535 /**((char *)0) = 1;*/
537 if (quant->mc_size > 256)
538 quant->mc_size = 256;
539 if (quant->mc_count > quant->mc_size)
540 quant->mc_count = quant->mc_size;
542 for (imgn = 0; imgn < count; ++imgn) {
543 if (imgn < opts->position_count) {
544 if (imgs[imgn]->xsize + opts->positions[imgn].x > scrw)
545 scrw = imgs[imgn]->xsize + opts->positions[imgn].x;
546 if (imgs[imgn]->ysize + opts->positions[imgn].y > scrw)
547 scrh = imgs[imgn]->ysize + opts->positions[imgn].y;
550 if (imgs[imgn]->xsize > scrw)
551 scrw = imgs[imgn]->xsize;
552 if (imgs[imgn]->ysize > scrh)
553 scrh = imgs[imgn]->ysize;
559 return 0; /* what are you smoking? */
561 orig_count = quant->mc_count;
562 orig_size = quant->mc_size;
564 if (opts->each_palette) {
567 /* we always generate a global palette - this lets systems with a
568 broken giflib work */
569 quant_makemap(quant, imgs, 1);
570 result = quant_translate(quant, imgs[0]);
572 want_trans = quant->transp != tr_none
573 && imgs[0]->channels == 4
574 && quant->mc_count < 256;
576 quant_transparent(quant, result, imgs[0], quant->mc_count);
578 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
581 mm_log((1, "Error in MakeMapObject."));
586 while (quant->mc_size > (1 << color_bits))
589 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
593 mm_log((1, "Error in EGifPutScreenDesc."));
598 if (!do_ns_loop(gf, opts))
601 if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
606 if (opts->position_count) {
607 posx = opts->positions[0].x;
608 posy = opts->positions[0].y;
612 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
613 opts->interlace, NULL) == GIF_ERROR) {
615 mm_log((1, "Error in EGifPutImageDesc."));
618 if (!do_write(gf, opts, imgs[0], result)) {
623 for (imgn = 1; imgn < count; ++imgn) {
624 quant->mc_count = orig_count;
625 quant->mc_size = orig_size;
626 quant_makemap(quant, imgs+imgn, 1);
627 result = quant_translate(quant, imgs[imgn]);
628 want_trans = quant->transp != tr_none
629 && imgs[imgn]->channels == 4
630 && quant->mc_count < 256;
632 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
634 if (!do_gce(gf, imgn, opts, want_trans, quant->mc_count)) {
639 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
642 mm_log((1, "Error in MakeMapObject."));
645 if (imgn < opts->position_count) {
646 posx = opts->positions[imgn].x;
647 posy = opts->positions[imgn].y;
651 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
652 imgs[imgn]->ysize, opts->interlace,
657 mm_log((1, "Error in EGifPutImageDesc."));
662 if (!do_write(gf, opts, imgs[imgn], result)) {
673 mm_log((1, "i_writegif_low: GOT HERE\n"));
675 /* handle the first image separately - since we allow giflib
676 conversion and giflib doesn't give us a separate function to build
679 /* produce a colour map */
680 quant_makemap(quant, imgs, count);
681 result = quant_translate(quant, imgs[0]);
683 /* get a palette entry for the transparency iff we have an image
684 with an alpha channel */
686 for (imgn = 0; imgn < count; ++imgn) {
687 if (imgs[imgn]->channels == 4) {
692 want_trans = want_trans && quant->transp != tr_none && quant->mc_count < 256;
693 if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
696 mm_log((1, "Error in MakeMapObject"));
700 while (quant->mc_count > (1 << color_bits))
703 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
707 mm_log((1, "Error in EGifPutScreenDesc."));
712 if (!do_ns_loop(gf, opts))
715 if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
720 if (opts->position_count) {
721 posx = opts->positions[0].x;
722 posy = opts->positions[0].y;
726 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
727 opts->interlace, NULL) == GIF_ERROR) {
729 mm_log((1, "Error in EGifPutImageDesc."));
732 if (want_trans && imgs[0]->channels == 4)
733 quant_transparent(quant, result, imgs[0], quant->mc_count);
735 if (!do_write(gf, opts, imgs[0], result)) {
742 for (imgn = 1; imgn < count; ++imgn) {
744 result = quant_translate(quant, imgs[imgn]);
745 local_trans = want_trans && imgs[imgn]->channels == 4;
747 quant_transparent(quant, result, imgs[imgn], quant->mc_count);
748 if (!do_gce(gf, imgn, opts, local_trans, quant->mc_count)) {
753 if (imgn < opts->position_count) {
754 posx = opts->positions[imgn].x;
755 posy = opts->positions[imgn].y;
759 if (EGifPutImageDesc(gf, posx, posy,
760 imgs[imgn]->xsize, imgs[imgn]->ysize,
761 opts->interlace, NULL) == GIF_ERROR) {
764 mm_log((1, "Error in EGifPutImageDesc."));
767 if (!do_write(gf, opts, imgs[imgn], result)) {
775 if (EGifCloseFile(gf) == GIF_ERROR) {
776 mm_log((1, "Error in EGifCloseFile\n"));
784 i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count,
788 mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d, opts %p)\n",
789 quant, fd, imgs, count, opts));
791 gif_set_version(quant, opts);
793 mm_log((1, "i_writegif_gen: set ops\n"));
795 if ((gf = EGifOpenFileHandle(fd)) == NULL) {
796 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
800 return i_writegif_low(quant, gf, imgs, count, opts);
805 static int gif_writer_callback(GifFileType *gf, const GifByteType *data, int size)
807 i_gen_write_data *gwd = (i_gen_write_data *)gf->UserData;
809 return i_gen_writer(gwd, data, size) ? size : 0;
815 i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
816 int maxlength, i_img **imgs, int count, i_gif_opts *opts)
820 i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
821 /* giflib declares this incorrectly as EgifOpen */
822 extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
825 mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d, opts %p)\n",
826 quant, cb, userdata, maxlength, imgs, count, opts));
828 if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
829 mm_log((1, "Error in EGifOpenFileHandle, unable to write image.\n"));
830 free_gen_write_data(gwd, 0);
834 result = i_writegif_low(quant, gf, imgs, count, opts);
835 return free_gen_write_data(gwd, result);