gif2png.c
上传用户:sorock1981
上传日期:2007-01-06
资源大小:73k
文件大小:22k
源码类别:

图片显示

开发平台:

Unix_Linux

  1. /*
  2.  * gif2png.c
  3.  * Copyright (C) 1995 Alexander Lehmann
  4.  * Bug fixes and enhancements by Greg Roelofs, 5 September 1999.
  5.  * Production release packaging by Eric S. Raymond <esr@thyrsus.com>.
  6.  * For conditions of distribution and use, see the file COPYING.
  7.  */
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <stdlib.h>
  11. #include <unistd.h> /* for isatty() */
  12. #define FALSE 0
  13. #define TRUE 1
  14. #define PNG_INTERNAL
  15. #include "gif2png.h"
  16. struct GIFelement first={NULL};
  17. struct GIFelement *current;
  18. int delete = FALSE;
  19. int optimize = FALSE;
  20. int histogram = FALSE;
  21. int interlaced = PNG_INTERLACE_LAST;
  22. int progress = FALSE;
  23. int verbose = FALSE;
  24. int recover = FALSE;
  25. int webconvert = FALSE;
  26. int software_chunk = TRUE;
  27. int filtermode = FALSE;
  28. int matte = FALSE;
  29. int gamma_srgb = FALSE;
  30. GifColor matte_color;
  31. long numgifs = 0L;
  32. long numpngs = 0L;
  33. int interlace_line(int height, int line)
  34. /* return the actual line # used to store an interlaced line */
  35. {
  36.     int res;
  37.     if ((line & 7) == 0) {
  38. return line >> 3;
  39.     }
  40.     res = (height+7) >> 3;
  41.     if ((line & 7) == 4) {
  42. return res+((line-4) >> 3);
  43.     }
  44.     res += (height+3) >> 3;
  45.     if ((line & 3) == 2) {
  46. return res + ((line-2) >> 2);
  47.     }
  48.     return res + ((height+1) >> 2) + ((line-1) >> 1);
  49. }
  50. int inv_interlace_line(int height, int line)
  51. /* inverse function of above, used for recovery of interlaced images */
  52. {
  53.     if ((line << 3)<height) {
  54. return line << 3;
  55.     }
  56.     line -= (height+7) >> 3;
  57.     if ((line << 3) + 4 < height) {
  58. return (line << 3) + 4;
  59.     }
  60.     line -= (height+3) >> 3;
  61.     if ((line << 2) + 2 < height) {
  62. return (line << 2) + 2;
  63.     }
  64.     line -= (height + 1) >> 2;
  65.     return (line << 1) + 1;
  66. }
  67. /* 
  68.  * Do some adjustments of the color palette.
  69.  *
  70.  * We won't check for duplicate colors, this is probably only a very
  71.  * rare case, but we check if all used colors are grays.  Some GIF
  72.  * writers (e.g. some versions of ppmtogif) leave the unused entries
  73.  * uninitialized, which breaks the detection of grayscale images
  74.  * (e.g. in QPV).
  75.  *
  76.  * If the image is grayscale we will use PNG color type 0, except when
  77.  * the transparency color is also appearing as a visible color. In
  78.  * this case we write a paletted image.  Another solution would be
  79.  * gray+alpha, but that would probably increase the image size too
  80.  * much.
  81.  *
  82.  * If there are only few gray levels (<=16), we try to create a 4-bit
  83.  * or less grayscale file, but if the gray levels do not fit into the
  84.  * necessary grid, we write a paletted image--e.g., if the image
  85.  * contains black, white and 50% gray, the grayscale image would
  86.  * require 8-bit, but the paletted image only 2-bit. Even with
  87.  * filtering, the 8-bit file will be larger.
  88.  */
  89. int is_gray(png_color c)
  90. {
  91.     /* check if r==g==b, moved to function to work around NeXTstep 3.3 cc bug.
  92.      * life could be so easy if at least the basic tools worked as expected.
  93.      */
  94.     return c.red == c.green && c.green == c.blue;
  95. }
  96. int writefile(struct GIFelement *s,struct GIFelement *e, FILE *fp, int lastimg)
  97. {
  98.     int i;
  99.     struct GIFimagestruct *img = e->imagestruct;
  100.     unsigned long *count = img->color_count;
  101.     GifColor *colors = img->colors;
  102.     int colors_used = 0;
  103.     byte remap[MAXCMSIZE];
  104.     int low_prec;
  105.     png_struct *png_ptr = xalloc(sizeof (png_struct));
  106.     png_info *info_ptr = xalloc(sizeof (png_info));
  107.     int p;
  108.     int gray_bitdepth;
  109.     png_color pal_rgb[MAXCMSIZE], *pltep;
  110.     png_byte pal_trans[MAXCMSIZE];
  111.     png_color_16 color16trans, color16back;
  112.     byte buffer[24]; /* used for gIFt and gIFg */
  113.     byte *data;
  114.     int j;
  115.     png_uint_16 histogr[MAXCMSIZE];
  116.     unsigned long hist_maxvalue;
  117.     int passcount;
  118.     int errorcount = 0;
  119.     png_text software;
  120.     /* these volatile declarations prevent gcc warnings ("variable might be
  121.      *  clobbered by `longjmp' or `vfork'") */
  122.     volatile int gray = TRUE;
  123.     volatile int last_color = 0;
  124.     volatile int remapping = FALSE;
  125.     volatile int bitdepth;
  126.     /* ignore the transparency value if no pixel in the image uses it */
  127.     if (img->trans != -1 && !count[img->trans])
  128. img->trans = -1;
  129.     if (optimize) {
  130. /* zero out unused color table entries and use zlib level 9 */
  131. for (i = 0; i < MAXCMSIZE; i++)
  132.     if(!count[i])
  133. colors[i].red = colors[i].green = colors[i].blue = 0;
  134. /*
  135.  * The way that last_color is computed ensures that trailing zeroed-out
  136.  * entries will be dropped.
  137.  * FIXME: someday, actually compact unused colors out of the table
  138.  * and remap pixels appropriately.
  139.  */
  140.     } else if (!recover) {
  141. /* report unused colors so user can choose to reconvert */
  142. int unused = 0;
  143. for (i = 0; i < MAXCMSIZE; i++)
  144.     if (!count[i])
  145. unused++;
  146. if (unused)
  147. {
  148.     fprintf(stderr, 
  149.     "gif2png: %d unused colors; convert with -O to removen",
  150.     unused);
  151.     errorcount++;
  152. }
  153.     }
  154.     for (i = 0; i < MAXCMSIZE; i++)
  155. if (count[i]) {
  156.     gray &= is_gray(colors[i]);
  157.     colors_used++;
  158.     if (last_color < i) 
  159. last_color = i;
  160. }
  161.     if (gray) {
  162. if (verbose > 1)
  163.     fprintf(stderr, "gif2png: input image is grayscalen");
  164. if (img->trans != -1) {
  165.     for (i = 0; i < MAXCMSIZE; i++) {
  166. if (i!=img->trans && colors[i].red == colors[img->trans].red) {
  167.     gray = FALSE;
  168.     if (verbose > 1)
  169. fprintf(stderr,
  170. "gif2png: transparent color is repeated in visible colors, using paletten");
  171.     break;
  172. }
  173.     }
  174. }
  175.     }
  176.     bitdepth = 8;
  177.     if (last_color<16) bitdepth = 4;
  178.     if (last_color<4) bitdepth = 2;
  179.     if (last_color<2) bitdepth = 1;
  180.     if (gray) {
  181. remapping = TRUE;
  182. for (i = 0; i < MAXCMSIZE; i++)
  183.     remap[i] = colors[i].red;
  184. gray_bitdepth = 8;
  185. /* try to adjust to 4-bit precision grayscale */
  186. low_prec = TRUE;
  187. for (i = 0; i < MAXCMSIZE; i++) {
  188.     if ((remap[i]&0xf)*0x11 != remap[i]) {
  189. low_prec = FALSE;
  190. break;
  191.     }
  192. }
  193. if (low_prec) {
  194.     for (i = 0; i < MAXCMSIZE; i++) {
  195. remap[i] &= 0xf;
  196.     }
  197.     gray_bitdepth = 4;
  198. }
  199. /* try to adjust to 2-bit precision grayscale */
  200. if (low_prec) {
  201.     for (i = 0; i < MAXCMSIZE; i++) {
  202. if ((remap[i]&3)*5 != remap[i]) {
  203.     low_prec = FALSE;
  204.     break;
  205. }
  206.     }
  207. }
  208. if (low_prec) {
  209.     for (i = 0; i < MAXCMSIZE; i++) {
  210. remap[i] &= 3;
  211.     }
  212.     gray_bitdepth = 2;
  213. }
  214. /* try to adjust to 1-bit precision grayscale */
  215. if (low_prec) {
  216.     for (i = 0; i < MAXCMSIZE; i++) {
  217. if ((remap[i]&1)*3 != remap[i]) {
  218.     low_prec = FALSE;
  219.     break;
  220. }
  221.     }
  222. }
  223. if (low_prec) {
  224.     for (i = 0; i < MAXCMSIZE; i++) {
  225. remap[i] &= 1;
  226.     }
  227.     gray_bitdepth=1;
  228. }
  229. if (bitdepth<gray_bitdepth) {
  230.     gray = FALSE; /* write palette file */
  231.     remapping = FALSE;
  232. } else {
  233.     bitdepth = gray_bitdepth;
  234. }
  235.     }
  236.     if (verbose > 1)
  237. fprintf(stderr, "gif2png: %d colors used, highest color %d, %s, bitdepth %dn",
  238. colors_used, last_color, gray ? "gray":"palette", bitdepth);
  239.     if (verbose > 2)
  240. for (i = 0; i <= last_color; i++) {
  241.     fprintf(stderr, "(%3d, %3d, %3d)n", 
  242.     colors[i].red, colors[i].blue, colors[i].green);  
  243. }
  244.     if (setjmp(png_ptr->jmpbuf)) {
  245. fprintf(stderr, "gif2png:  libpng returns fatal error conditionn");
  246. free(png_ptr);
  247. free(info_ptr);
  248. return 1;
  249.     }
  250.     /* initialize PNG writing */
  251.     png_write_init(png_ptr);
  252.     png_info_init(info_ptr);
  253.     png_init_io(png_ptr, fp);
  254.     if(optimize)
  255.        png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
  256.     if (gamma_srgb)
  257.     {
  258.           png_set_gAMA(png_ptr, info_ptr, 1./2.2);
  259. #if (PNG_LIBPNG_VER > 96)
  260.   png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
  261. #endif
  262.     }
  263.     /* set basic image properties: width/height, type, bit depth, interlace */
  264.     if (interlaced == PNG_INTERLACE_LAST)
  265. interlaced = current->imagestruct->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
  266.     png_set_IHDR(png_ptr, info_ptr, 
  267.  current->imagestruct->width, current->imagestruct->height,
  268.  bitdepth, 
  269.  gray ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_PALETTE,
  270.  interlaced, 
  271.  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  272.     /* if the GIF specified an aspect ratio, turn it into a pHYs chunk */
  273.     if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49)
  274. png_set_pHYs(png_ptr, info_ptr, 
  275.      GifScreen.AspectRatio+15, 64, PNG_RESOLUTION_UNKNOWN);
  276.     /* if the GIF specified an image offset, turn it into a oFFs chunk */
  277.     if (img->offset_x>0 && img->offset_y>0)
  278. png_set_oFFs(png_ptr, info_ptr, 
  279.      img->offset_x, img->offset_y, PNG_OFFSET_PIXEL);
  280.     if (GifScreen.Background > 0) { /* no background for palette entry 0 */
  281. /* if the background color doesn't appear in local palette, we just
  282.    leave it out, if the pic is part of an animation, at least some will
  283.    use the global palette */
  284. if (gray) {
  285.     if (is_gray(GifScreen.ColorMap[GifScreen.Background])) {
  286. color16back.gray = remap[GifScreen.Background];
  287. png_set_bKGD(png_ptr, info_ptr, &color16back);
  288.     }
  289. } else {
  290.     for (i = 0; i < MAXCMSIZE; i++) {
  291. if (GifScreen.ColorMap[GifScreen.Background].red == colors[i].red &&
  292.     GifScreen.ColorMap[GifScreen.Background].green == colors[i].green &&
  293.     GifScreen.ColorMap[GifScreen.Background].blue == colors[i].blue) {
  294.     if (last_color < i) 
  295. last_color = i;
  296.     color16back.index = i;
  297.     info_ptr->background = color16back;
  298.     info_ptr->valid |= PNG_INFO_bKGD;
  299.     break;
  300. }
  301.     }
  302. }
  303.     }
  304.     /* transparency handling */
  305.     if (img->trans != -1) {
  306. if (gray) {
  307.     if (verbose > 2)
  308. fprintf(stderr, "gif2png: handling grayscale transparencyn");
  309.     color16trans.gray = remap[img->trans];
  310.     png_set_tRNS(png_ptr, info_ptr, NULL, 0, &color16trans);
  311. } else {
  312.     if (verbose > 2)
  313. fprintf(stderr, "gif2png: handling palette transparencyn");
  314. #if 0
  315.     for (i = 0; i < MAXCMSIZE; i++)
  316. pal_trans[i] = 255;
  317.     pal_trans[img->trans] = 0;
  318.     png_set_tRNS(png_ptr, info_ptr, pal_trans, img->trans+1, NULL);
  319. #else
  320.     /* GRR NOTE:  may be inconsistent with background-color logic (above) */
  321.     remapping = TRUE;
  322.     for (i = 0; i < MAXCMSIZE; i++)
  323. remap[i] = i;
  324.     remap[0] = img->trans;
  325.     remap[img->trans] = 0;
  326.     pal_trans[0] = 0;
  327.     png_set_tRNS(png_ptr, info_ptr, pal_trans, 1, NULL);
  328. #endif
  329. }
  330.     }
  331.     /* generate the PNG palette */
  332.     if (!gray) {
  333. if (remapping) {
  334.     if (verbose > 2)
  335. fprintf(stderr, "Remapping palette...n");
  336.     for (i = 0; i <= last_color; i++) {
  337. pal_rgb[i].red   = current->imagestruct->colors[remap[i]].red;
  338. pal_rgb[i].green = current->imagestruct->colors[remap[i]].green;
  339. pal_rgb[i].blue  = current->imagestruct->colors[remap[i]].blue;
  340.     }
  341.     pltep = pal_rgb;
  342. } else {
  343.     if (verbose > 2)
  344. fprintf(stderr, "Palette copied from GIF colors...n");
  345.     pltep = current->imagestruct->colors;
  346. }
  347. png_set_PLTE(png_ptr, info_ptr, pltep, last_color+1);
  348. if (verbose > 2) {
  349.     fprintf(stderr,"PNG palette generated with %d colors:n",last_color+1);
  350.     for (i = 0; i <= last_color; i++) {
  351. fprintf(stderr, "(%3d, %3d, %3d)n", 
  352. pltep[i].red, pltep[i].blue, pltep[i].green);  
  353.     }
  354. }
  355.     }
  356.     /* generate histogram chunk for paletted images only */
  357.     if (histogram && !gray) {
  358. hist_maxvalue = 0;
  359. for (i = 0; i < MAXCMSIZE; i++)
  360.     if (count[i]>hist_maxvalue)
  361. hist_maxvalue = count[i];
  362. if (hist_maxvalue <= 65535) {
  363.     /* no scaling necessary */
  364.     for (i = 0; i < MAXCMSIZE; i++)
  365. histogr[i] = (png_uint_16)count[i];
  366. } else {
  367.     for (i = 0; i < MAXCMSIZE; i++)
  368. if (count[i]) {
  369.     histogr[i] = (png_uint_16)
  370. #ifdef __GO32__
  371. /* avoid using fpu instructions on djgpp, so we don't need emu387 */
  372. (((long long)count[i]*65535)/hist_maxvalue);
  373. #else
  374.     ((double)count[i]*65535.0/hist_maxvalue);
  375. #endif
  376.     if (histogr[i] == 0)
  377. histogr[i] = 1;
  378. } else {
  379.     histogr[i] = 0;
  380. }
  381. }
  382. png_set_hIST(png_ptr, info_ptr, histogr);
  383.     }
  384.     /* transcribe software chunk if necessary */
  385.     if (software_chunk) {
  386. software.compression = PNG_TEXT_COMPRESSION_NONE;
  387. software.key = "Software";
  388. software.text = (char*)version;
  389. software.text_length = strlen(software.text);
  390. png_set_text(png_ptr, info_ptr, &software, 1);
  391.     }
  392.     png_write_info(png_ptr, info_ptr);
  393.     if (info_ptr->bit_depth<8)
  394. png_set_packing(png_ptr);
  395.     /* loop over elements until we reach the image or the last element if
  396.        this is the last image in a GIF */
  397.     while(lastimg ? s != NULL : s != e->next) {
  398. switch((byte)s->GIFtype) {
  399. case GIFimage:
  400.     passcount = png_set_interlace_handling(png_ptr);
  401.     for (p = 0; p<passcount;p++)
  402. for (i = 0; i<current->imagestruct->height; i++) {
  403.     if (progress) {
  404. if (passcount>1)
  405.     fprintf(stderr, "%d/%d ", p+1, passcount);
  406. fprintf(stderr, "%2d%%r",
  407. (int)(((long)i*100)/current->imagestruct->height));
  408. fflush(stderr);
  409.     }
  410.     data = access_data(current, (long) (img->interlace ?
  411. interlace_line(current->imagestruct->height,i) : i) *
  412.        current->imagestruct->width,
  413.        current->imagestruct->width);
  414. #ifndef TMPFILE
  415.     /* if we store the image in memory, we have to remap once */
  416.     if (remapping && p == 0)
  417. #else
  418. /* if we store the image in a file, we have to remap each time */
  419. if (remapping)
  420. #endif
  421. {
  422.     for (j = 0; j < img->width; j++) {
  423. data[j] = remap[data[j]];
  424.     }
  425. }
  426.     png_write_row(png_ptr, data);
  427. }
  428.     break;
  429. case GIFcomment:
  430.     data = access_data(s, 0, s->size);
  431.     j = s->size;
  432.     if (data[j-1] == '')  /* some apps include a NULL in GIF comment */
  433. --j;
  434.     if (j<500) {
  435. png_write_tEXt(png_ptr, "Comment", (png_charp)data, j);
  436.     } else {
  437. png_write_zTXt(png_ptr, "Comment", (png_charp)data, j, 0);
  438.     }
  439.     break;
  440. case GIFapplication:
  441.     data = access_data(s, 0, s->size);
  442.     png_write_chunk_start(png_ptr, "gIFx", s->size);
  443.     png_write_chunk_data(png_ptr, data, s->size);
  444.     png_write_chunk_end(png_ptr);
  445.     break;
  446. /* Plain Text Extensions are not supported because they represent a separate
  447.    subimage, and PNG is explicitly a single-image format.  PNG's gIFt chunk
  448.    has been officially deprecated.
  449. */
  450. #if 0
  451. case GIFplaintext:
  452.     data = access_data(s, 0, s->size);
  453.     /* gIFt is 12 bytes longer than GCE, due to 32-bit ints and
  454.        rgb colors */
  455.     png_write_chunk_start(png_ptr, "gIFt", s->size+12);
  456.     memset(buffer, 0, 24);
  457.     buffer[2] = data[1];
  458.     buffer[3] = data[0];
  459.     buffer[6] = data[3];
  460.     buffer[7] = data[2];
  461.     buffer[10] = data[5];
  462.     buffer[11] = data[4];
  463.     buffer[14] = data[7];
  464.     buffer[15] = data[6];
  465.     buffer[16] = data[8];
  466.     buffer[17] = data[9];
  467.     buffer[18] = GifScreen.ColorMap[data[10]].red;
  468.     buffer[19] = GifScreen.ColorMap[data[10]].green;
  469.     buffer[20] = GifScreen.ColorMap[data[10]].blue;
  470.     buffer[21] = GifScreen.ColorMap[data[11]].red;
  471.     buffer[22] = GifScreen.ColorMap[data[11]].green;
  472.     buffer[23] = GifScreen.ColorMap[data[11]].blue;
  473.     png_write_chunk_data(png_ptr, buffer, 24);
  474.     png_write_chunk_data(png_ptr, data+12, s->size-12);
  475.     png_write_chunk_end(png_ptr);
  476.     break;
  477. #endif
  478. case GIFgraphicctl:
  479.     data = access_data(s, 0, s->size);
  480.     buffer[0] = (data[0] >> 2) & 7;
  481.     buffer[1] = (data[0] >> 1) & 1;
  482.     buffer[2] = data[2];
  483.     buffer[3] = data[1];
  484.     png_write_chunk(png_ptr, "gIFg", buffer, 4);
  485.     break;
  486. default:
  487.     fprintf(stderr, "gif2png internal error: encountered unused element type %cn", s->GIFtype);
  488.     break;
  489. }
  490. s = s->next;
  491.     }
  492.     png_write_end(png_ptr, info_ptr);
  493.     png_write_destroy(png_ptr);
  494.     free(png_ptr);
  495.     free(info_ptr);
  496.     return errorcount;
  497. }
  498. int
  499. MatteGIF(GifColor matte)
  500. /* convert transparent pixels to a matte of given color */
  501. {
  502.     int icount = 0;
  503.     /* apply matte color to image-local transparency values */
  504.     for (current = &first; current ; current = current->next) {
  505. if (current->imagestruct) {
  506.     if (current->imagestruct->trans == -1)
  507. fprintf(stderr, 
  508. "gif2png: no transparency color in image %d, matte argument ignoredn",
  509. icount);
  510.     else if (verbose) {
  511. fprintf(stderr, 
  512. "gif2png: transparent value in image %d is %dn",  
  513. icount, current->imagestruct->trans);
  514.     }
  515.     current->imagestruct->colors[current->imagestruct->trans] = matte;
  516.     current->imagestruct->trans = -1;
  517. }
  518.     }
  519.     return icount;
  520. }
  521. int processfilter(void)
  522. {
  523.     int num_pics;
  524.     struct GIFelement *start;
  525.     current = &first;
  526.     num_pics = ReadGIF (stdin);
  527.     fclose(stdin);
  528.     if (num_pics != 1) {
  529. fprintf(stderr, "gif2png: %d images in -1 (oneonly) moden", num_pics);
  530. return 1;
  531.     }
  532.     /* eliminate use of transparency, if that is called for */
  533.     if (matte)
  534. MatteGIF(matte_color);
  535.     start = NULL;
  536.     for (current = first.next; current ; current = current->next) {
  537. if (start == NULL) start = current;
  538. if (current->GIFtype == GIFimage) {
  539.     (void)writefile(start, current, stdout, TRUE);
  540.     start = NULL;
  541.     ++numpngs;
  542. }
  543.     }
  544.     free_mem();
  545.     return 0;
  546. }
  547. int processfile(char *fname, FILE *fp)
  548. {
  549.     char outname[1025]; /* MAXPATHLEN comes under too many names */
  550.     int num_pics;
  551.     struct GIFelement *start;
  552.     int i, suppress_delete = FALSE;
  553.     char *file_ext;
  554.     if (fp == NULL) return 1;
  555. #ifdef TMPFILE
  556.     fseek(tempfile, 0, SEEK_SET);
  557. #endif
  558.     current = &first;
  559.     num_pics = ReadGIF (fp);
  560.     fclose(fp);
  561.     if (num_pics >= 0 && verbose > 1)
  562. fprintf(stderr, "gif2png: number of images %dn", num_pics);
  563.     if (num_pics <= 0)
  564. return 1;
  565.     if (webconvert)
  566. if (num_pics != 1)
  567. {
  568.     fprintf(stderr, "gif2png: %s is multi-imagen", fname);
  569.     return 0;
  570. }
  571. else if (current->imagestruct && current->imagestruct->trans != -1)
  572. {
  573.     fprintf(stderr, "gif2png: %s has a transparency colorn", fname);
  574.     return 0;
  575. }
  576. else
  577. {
  578.     printf("%sn", fname);
  579.     return 0;
  580. }
  581.     /* eliminate use of transparency, if that is called for */
  582.     if (matte)
  583. MatteGIF(matte_color);
  584.     /* create output filename */
  585.     strcpy(outname, fname);
  586.     file_ext = outname+strlen(outname)-4;
  587.     if (strcmp(file_ext, ".gif") != 0 && strcmp(file_ext, ".GIF") != 0 &&
  588. strcmp(file_ext, "_gif") != 0 && strcmp(file_ext, "_GIF") != 0) {
  589. /* try to derive basename */
  590. file_ext = outname+strlen(outname);
  591. while(file_ext >= outname) {
  592.     if (*file_ext == '.' || *file_ext == '/' || *file_ext == '\') break;
  593.     file_ext--;
  594. }
  595. if (file_ext<outname || *file_ext != '.') {
  596.     /* as a last resort, just add .png to the filename */
  597.     file_ext = outname+strlen(outname);
  598. }
  599.     }
  600.     strcpy(file_ext, ".png"); /* images are named .png, .p01, .p02, ... */
  601.     start = NULL;
  602.     i = 0;
  603.     for (current = first.next; current ; current = current->next) {
  604. if (start == NULL) start = current;
  605. if (current->GIFtype == GIFimage) {
  606.     i++;
  607.     if ((fp = fopen(outname, "wb")) == NULL) {
  608. perror(outname);
  609. return 1;
  610.     } else {
  611. suppress_delete |= writefile(start, current, fp, i == num_pics);
  612. fclose(fp);
  613. ++numpngs;
  614. start = NULL;
  615. sprintf(file_ext, ".p%02d", i);
  616.     }
  617. }
  618.     }
  619.     free_mem();
  620.     if (delete && !suppress_delete) {
  621. unlink(fname);
  622.     }
  623.     return 0;
  624. }
  625. /* check if stdin is a terminal, if your compiler is lacking isatty or fileno
  626.    (non-ANSI functions), just return 0
  627. */
  628. int input_is_terminal(void)
  629. {
  630.     return isatty(fileno(stdin));
  631. }
  632. int main(int argc, char *argv[])
  633. {
  634.     FILE *fp;
  635.     int i, background;
  636.     int errors = 0;
  637.     char name[1025];
  638.     int ac;
  639.     char *color;
  640.     software_chunk = 1;
  641.     ac = 1;
  642.     for (ac = 1; ac<argc && argv[ac][0] == '-'; ac++)
  643.     {
  644. for (i = 1;i<strlen(argv[ac]); i++)
  645.     switch(argv[ac][i]) {
  646.     case 'b':
  647. if (ac >= argc - 1)
  648. {
  649.     fputs("gif2png: missing background-matte argumentn",
  650.   stderr);
  651.     exit(1);
  652. }
  653. color = argv[++ac];
  654. if (*color == '#')
  655.     color++;
  656.         sscanf(color, "%x", &background);
  657. matte_color.red = (background >> 16) & 0xff;
  658. matte_color.green = (background >> 8) & 0xff; 
  659. matte_color.blue = background & 0xff;
  660. matte = TRUE;
  661. goto skiparg;
  662.     case 'd':
  663. delete = TRUE;
  664. break;
  665.     case 'f':
  666. filtermode = TRUE;
  667. break;
  668.     case 'g':
  669. gamma_srgb = TRUE;
  670. break;
  671.     case 'h':
  672. histogram = TRUE;
  673. break;
  674.     case 'i':
  675. interlaced = PNG_INTERLACE_ADAM7;
  676. break;
  677.     case 'n':
  678. interlaced = PNG_INTERLACE_NONE;
  679. break;
  680.     case 'p':
  681. progress = TRUE;
  682. break;
  683.     case 'r':
  684. recover = TRUE;
  685. break;
  686.     case 's':
  687. software_chunk = FALSE;
  688. break;
  689.     case 'v':
  690. ++verbose;
  691. break;
  692.     case 'w':
  693. webconvert = TRUE;
  694. break;
  695.     case 'O':
  696. optimize = TRUE;
  697. break;
  698.     default:
  699. fprintf(stderr, "gif2png: unknown option `-%c'n", argv[ac][i]);
  700.     usage:
  701. fprintf(stderr,
  702. "n"
  703. "usage: gif2png [ -bdfghinprsvwO ] [file[.gif]] ...n"
  704. "n"
  705. "   -b  replace background pels with given RRGGBB color (hex digits)n"
  706. "   -d  delete source GIF files after successful conversionn"
  707. "   -g  write gamma=(1.0/2.2) and sRGB chunksn"
  708. "   -h  generate PNG histogram chunks into color output filesn"
  709. "   -i  force conversion to interlaced PNG filesn"
  710. "   -n  force conversion to noninterlaced PNG filesn"
  711. "   -p  display progress of PNG writing in %%n"
  712. "   -r  try to recover corrupted GIF filesn"
  713. "   -s  do not write Software text chunkn"
  714. "   -v  verbose; display conversion statistics and debugging messagesn"
  715. "   -w  web probe, list images without animation or transparencyn"
  716. "   -O  optimize; remove unused color-table entries; use zlib level 9n"
  717. "   -1  one-only; allow use as filter, die on multi-image GIFn"
  718. "n"
  719. "   If no source files are listed, stdin is converted to noname.png.n"
  720. "n"
  721. "   This is %s, %sn", version, compile_info);
  722. return 1;
  723.     }
  724.     skiparg:;
  725.     }
  726.     if (verbose > 1)
  727. fprintf(stderr, "gif2png: %s, %sn", version, compile_info);
  728. #ifdef TMPFILE
  729.     if ((tempfile = tmpfile()) == NULL) {
  730. perror("tempfile");
  731. exit(1);
  732.     }
  733. #endif
  734.     /* loop over arguments or use stdin */
  735.     if (argc == ac) {
  736. if (input_is_terminal()) {
  737.     goto usage;
  738. }
  739. if (verbose > 1)
  740.     fprintf(stderr, "stdin:n");
  741. if (filtermode) {
  742.     if (processfilter() != 0) ++errors;
  743. } else {
  744.     processfile("noname.gif", stdin);
  745.     ++numgifs;
  746. }
  747.     } else {
  748. for (i = ac;i<argc; i++) {
  749.     strcpy(name, argv[i]);
  750.     if ((fp = fopen(name, "rb")) == NULL) {
  751. /* retry with .gif appended */
  752. strcat(name, ".gif");
  753. fp = fopen(name,"rb");
  754.     }
  755.     if (fp == NULL) {
  756. perror(argv[i]);
  757. errors = 1;
  758.     } else {
  759. if (verbose > 1)
  760.     fprintf(stderr, "%s:n", name);
  761. errors |= processfile(name, fp);
  762. /* fp is closed in processfile */
  763. ++numgifs;
  764.     }
  765. }
  766.     }
  767.     if (verbose)
  768. fprintf(stderr, "Done (%s).  Converted %ld GIF%s into %ld PNG%s.n",
  769. errors? "with one or more errors" : "no errors detected", 
  770. numgifs, (numgifs == 1) ? "" : "s", numpngs, (numpngs == 1)? "" : "s");
  771.     return errors;
  772. }