[comp.sources.x] v03i012: program to display gif images, Patch1

mikew@wyse.wyse.com (Mike Wexler) (02/09/89)

Submitted-by: bradley@dsl.cis.upenn.edu (John Bradley)
Posting-number: Volume 3, Issue 12
Archive-name: xgif/patch1

[I don't have xgif, so I didn't test this and I don't know where to get
it. -mcw]

This patch fixes a bogosity in the xgif code where it would attempt to allocate
all the colors in the GIF colormap.  This would always fail on 8-bit GIF 
images, and the program would strip color bits off to reduce the number of 
colors needed.

This patch uses the somewhat more intellegent approach of only trying to 
allocate the colors that the GIF picture USES.  In many cases, this is far less
than the number of colors in the GIF colormap.

John Bradley  -  bradley@cis.upenn.edu


---------------------------------------------------------
diff -c xgif/README xgifnew/README
*** xgif/README	Wed Feb  8 17:29:20 1989
--- xgifnew/README	Fri Feb  3 21:00:43 1989
***************
*** 6,8 ****
--- 6,18 ----
  
  John Bradley  -  bradley@cis.upenn.edu
  
+ Revision History:
+ 
+ PATCH 1  -  Somewhat more intellegent color use.  Basically, it only trys
+ to allocate the colors that the GIF picture actually USES, rather than one 
+ color for each entry in the GIF colormap.  Ran into a bunch of pictures that
+ only used 16 colors out of a 256-entry colormap...
+ 
+ Also added [-display] option, in keeping with the New Improved Standard
+ Command Line Options for X11.
+ 
diff -c xgif/patchlevel.h xgifnew/patchlevel.h
*** xgif/patchlevel.h	Wed Feb  8 17:29:21 1989
--- xgifnew/patchlevel.h	Fri Feb  3 19:53:55 1989
***************
*** 1 ****
! #define PATCHLEVEL 0
--- 1 ----
! #define PATCHLEVEL 1
diff -c xgif/xgif.c xgifnew/xgif.c
*** xgif/xgif.c	Wed Feb  8 17:29:16 1989
--- xgifnew/xgif.c	Fri Feb  3 21:08:07 1989
***************
*** 30,65 ****
      for (i = 1; i < argc; i++) {
          char *strind;
  
!         if (argv[i][0] == '=') {
              geom = argv[i];
              continue;
              }
  
!         if (!strncmp(argv[i],"-g",2)) {		/* geometry */
!             i++;
              geom = argv[i];
              continue;
              }
  
!         strind = index(argv[i], ':');
          if(strind != NULL) {
              display = argv[i];
              continue;
              }
  
!         if (!strcmp(argv[i],"-e")) {
              i++;
              expand=atoi(argv[i]);
              continue;
              }
  
!         if (!strcmp(argv[i],"-s")) {
              i++;
              strip=atoi(argv[i]);
              continue;
              }
  
!         if (!strcmp(argv[i],"-ns")) {
              nostrip++;
              continue;
              }
--- 30,71 ----
      for (i = 1; i < argc; i++) {
          char *strind;
  
!         if (!strncmp(argv[i],"-g",2)) {		/* geometry */
!             i++;
              geom = argv[i];
              continue;
              }
  
!         if (argv[i][0] == '=') {		/* old-style geometry */
              geom = argv[i];
              continue;
              }
  
!         if (!strncmp(argv[i],"-d",2)) {		/* display */
!             i++;
!             display = argv[i];
!             continue;
!             }
! 
!         strind = index(argv[i], ':');		/* old-style display */
          if(strind != NULL) {
              display = argv[i];
              continue;
              }
  
!         if (!strcmp(argv[i],"-e")) {		/* expand */
              i++;
              expand=atoi(argv[i]);
              continue;
              }
  
!         if (!strcmp(argv[i],"-s")) {		/* strip */
              i++;
              strip=atoi(argv[i]);
              continue;
              }
  
!         if (!strcmp(argv[i],"-ns")) {		/* nostrip */
              nostrip++;
              continue;
              }
***************
*** 94,101 ****
      theVisual = DefaultVisual(theDisp,theScreen);
  
      dispcells = DisplayCells(theDisp, theScreen);
!     if (dispcells<255) 
!         FatalError("This program requires an 8-plane display, at least.");
  
  
      /****************** Open/Read the File  *****************/
--- 100,107 ----
      theVisual = DefaultVisual(theDisp,theScreen);
  
      dispcells = DisplayCells(theDisp, theScreen);
!     if (dispcells<=2) 
!         FatalError("This program requires a color display, pref. 8 bits.");
  
  
      /****************** Open/Read the File  *****************/
***************
*** 186,192 ****
  /***********************************/
  Syntax()
  {
!     printf("Usage: %s filename [=geometry | -geometry geom] [display]\n",cmd);
      printf("       [-e 1..%d] [-s 0-7] [-ns]\n",MAXEXPAND);
      exit(1);
  }
--- 192,198 ----
  /***********************************/
  Syntax()
  {
!     printf("Usage: %s filename [[-geometry] geom] [[-display] display]\n",cmd);
      printf("       [-e 1..%d] [-s 0-7] [-ns]\n",MAXEXPAND);
      exit(1);
  }
diff -c xgif/xgifload.c xgifnew/xgifload.c
*** xgif/xgifload.c	Wed Feb  8 17:29:18 1989
--- xgifnew/xgifload.c	Fri Feb  3 21:30:13 1989
***************
*** 72,78 ****
  int OutCode[1025];
  
      /* The color map, read from the GIF header */
! byte Red[256], Green[256], Blue[256];
  
  char *id = "GIF87a";
  
--- 72,79 ----
  int OutCode[1025];
  
      /* The color map, read from the GIF header */
! byte Red[256], Green[256], Blue[256], used[256];
! int  numused;
  
  char *id = "GIF87a";
  
***************
*** 86,94 ****
      int            filesize;
      register byte  ch, ch1;
      register byte *ptr, *ptr1;
!     register int   i,j;
!     static byte    lmasks[8] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80};
!     byte           lmask;
  
      if (strcmp(fname,"-")==0) { fp = stdin;  fname = "<stdin>"; }
                           else fp = fopen(fname,"r");
--- 87,93 ----
      int            filesize;
      register byte  ch, ch1;
      register byte *ptr, *ptr1;
!     register int   i;
  
      if (strcmp(fname,"-")==0) { fp = stdin;  fname = "<stdin>"; }
                           else fp = fopen(fname,"r");
***************
*** 147,230 ****
  	    Red[i] = NEXTBYTE;
  	    Green[i] = NEXTBYTE;
  	    Blue[i] = NEXTBYTE;
  	    }
  
!         /* Allocate the X colors for this picture */
! 
!         if (nostrip)  {   /* nostrip was set.  try REAL hard to do it */
!             j = 0;
!             lmask = lmasks[strip];
!             for (i=0; i<numcols; i++) {
!                 defs[i].red   = (Red[i]  &lmask)<<8;
!                 defs[i].green = (Green[i]&lmask)<<8;
!                 defs[i].blue  = (Blue[i] &lmask)<<8;
!                 defs[i].flags = DoRed | DoGreen | DoBlue;
!                 if (!XAllocColor(theDisp,theCmap,&defs[i])) { 
!                     j++;  defs[i].pixel = 0xffff;
!                     }
!                 cols[i] = defs[i].pixel;
!                 }
! 
!             if (j) {		/* failed to pull it off */
!                 XColor ctab[256];
! 
!                 fprintf(stderr,"failed to allocate %d out of %d colors.  Trying extra hard.\n",j,numcols);
!                 
!                 /* read in the color table */
!                 for (i=0; i<numcols; i++) ctab[i].pixel = i;
!                 XQueryColors(theDisp,theCmap,ctab,numcols);
!                 
!                 for (i=0; i<numcols; i++)
!                     if (cols[i] == 0xffff) {		/* an unallocated pixel */
!                         int d, mdist, close;
!                         unsigned long r,g,b;
! 
!                         mdist = 100000;   close = -1;
!                         r =  Red[i];
!                         g =  Green[i];
!                         b =  Blue[i];
!                         for (j=0; j<numcols; j++) {
!                             d = abs(r - (ctab[j].red>>8)) +
!                                 abs(g - (ctab[j].green>>8)) +
!                                 abs(b - (ctab[j].blue>>8));
!                             if (d<mdist) { mdist=d; close=j; }
!                             }
!                         if (close<0) FatalError("simply can't do it.  Sorry.");
!                         bcopy(&defs[close],&defs[i],sizeof(XColor));
!                         cols[i] = ctab[close].pixel;
!                         }
!                 }
!             }
  
-         else {          /* strip wasn't set, do the best auto-strip */
-             j = 0;
-             while (strip<8) {
-                 lmask = lmasks[strip];
-                 for (i=0; i<numcols; i++) {
-                     defs[i].red   = (Red[i]  &lmask)<<8;
-                     defs[i].green = (Green[i]&lmask)<<8;
-                     defs[i].blue  = (Blue[i] &lmask)<<8;
-                     defs[i].flags = DoRed | DoGreen | DoBlue;
-                     if (!XAllocColor(theDisp,theCmap,&defs[i])) break;
-                     cols[i] = defs[i].pixel;
-                     }
-                 if (i<numcols) {		/* failed */
-                     strip++;  j++;
-                     XFreeColors(theDisp,theCmap,cols,i,0L);
-                     }
-                 else break;
-                 }
- 
-             if (j && strip<8)
-                 fprintf(stderr,"%s:  %s stripped %d bits\n",cmd,fname,strip);
- 
-             if (strip==8) {
-                 fprintf(stderr,"UTTERLY failed to allocate the desired colors.  Sorry.\n");
-                 for (i=0; i<numcols; i++) cols[i]=i;
-                 }
-             }
-         }
- 
      else {  /* no colormap in GIF file */
          fprintf(stderr,"%s:  warning!  no colortable in this file.  Winging it.\n",cmd);
          if (!numcols) numcols=256;
--- 146,157 ----
  	    Red[i] = NEXTBYTE;
  	    Green[i] = NEXTBYTE;
  	    Blue[i] = NEXTBYTE;
+             used[i] = 0;
  	    }
+         numused = 0;
  
!         }
  
      else {  /* no colormap in GIF file */
          fprintf(stderr,"%s:  warning!  no colortable in this file.  Winging it.\n",cmd);
          if (!numcols) numcols=256;
***************
*** 253,260 ****
  		Width, Height, (Interlace) ? "" : "non-");
  
      else 
!         fprintf(stderr, "%s:  %s is %dx%d, %d colors, %sinterlaced\n",
! 	   cmd, fname, Width,Height,ColorMapSize,(Interlace) ? "" : "non-");
      
  
  /* Note that I ignore the possible existence of a local color map.
--- 180,187 ----
  		Width, Height, (Interlace) ? "" : "non-");
  
      else 
!         fprintf(stderr, "%s:  %s is %dx%d, %d colors  ",
! 	   cmd, fname, Width,Height,ColorMapSize);
      
  
  /* Note that I ignore the possible existence of a local color map.
***************
*** 356,363 ****
   */
  
  	    while (CurCode > BitMask) {
! 		if (OutCount > 1024)
! 		    FatalError("corrupt GIF file (OutCount)");
  		OutCode[OutCount++] = Suffix[CurCode];
  		CurCode = Prefix[CurCode];
  	    }
--- 283,292 ----
   */
  
  	    while (CurCode > BitMask) {
! 		if (OutCount > 1024) {
! 		    fprintf(stderr,"\nCorrupt GIF file (OutCount)!\n");
!                     _exit(-1);  /* calling 'exit(-1)' dumps core, so I don't */
!                     }
  		OutCode[OutCount++] = Suffix[CurCode];
  		CurCode = Prefix[CurCode];
  	    }
***************
*** 402,410 ****
--- 331,343 ----
  
      if (Verbose)
  	fprintf(stderr, "done.\n");
+     else
+         fprintf(stderr,"(of which %d are used)\n",numused);
  
      if (fp != stdin)
  	fclose(fp);
+ 
+     ColorDicking(fname);
  }
  
  
***************
*** 432,439 ****
  AddToPixel(Index)
  byte Index;
  {
!     *(Image + YC * BytesPerScanline + XC) = cols[Index&(numcols-1)];
  
  /* Update the X-coordinate, and if it overflows, update the Y-coordinate */
  
      if (++XC == Width) {
--- 365,375 ----
  AddToPixel(Index)
  byte Index;
  {
!     if (YC<Height)
!         *(Image + YC * BytesPerScanline + XC) = Index;
  
+     if (!used[Index]) { used[Index]=1;  numused++; }
+ 
  /* Update the X-coordinate, and if it overflows, update the Y-coordinate */
  
      if (++XC == Width) {
***************
*** 477,480 ****
--- 413,523 ----
  	    }
  	}
      }
+ }
+ 
+ 
+ 
+ /*************************/
+ ColorDicking(fname)
+ char *fname;
+ {
+     /* we've got the picture loaded, we know what colors are needed. get 'em */
+ 
+     register int   i,j;
+     static byte    lmasks[8] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80};
+     byte           lmask, *ptr;
+ 
+ 
+     if (!HasColormap) return;
+     /* no need to allocate any colors if no colormap in GIF file */
+ 
+     /* Allocate the X colors for this picture */
+ 
+     if (nostrip)  {   /* nostrip was set.  try REAL hard to do it */
+         for (i=j=0; i<numcols; i++) {
+             if (used[i]) {
+                 defs[i].red   = Red[i]<<8;
+                 defs[i].green = Green[i]<<8;
+                 defs[i].blue  = Blue[i]<<8;
+                 defs[i].flags = DoRed | DoGreen | DoBlue;
+                 if (!XAllocColor(theDisp,theCmap,&defs[i])) { 
+                     j++;  defs[i].pixel = 0xffff;
+                     }
+                 cols[i] = defs[i].pixel;
+                 }
+             }
+ 
+         if (j) {		/* failed to pull it off */
+             XColor ctab[256];
+             int    dc;
+ 
+             dc = (dispcells<256) ? dispcells : 256;
+ 
+             fprintf(stderr,"failed to allocate %d out of %d colors.  Trying extra hard.\n",j,numused);
+ 
+             /* read in the color table */
+             for (i=0; i<dc; i++) ctab[i].pixel = i;
+             XQueryColors(theDisp,theCmap,ctab,dc);
+                 
+             /* run through the used colors.  any used color that has a pixel
+                value of 0xffff wasn't allocated.  for such colors, run through
+                the entire X colormap and pick the closest color */
+ 
+             for (i=0; i<numcols; i++)
+                 if (used[i] && cols[i]==0xffff) {  /* an unallocated pixel */
+                     int d, mdist, close;
+                     unsigned long r,g,b;
+ 
+                     mdist = 100000;   close = -1;
+                     r =  Red[i];
+                     g =  Green[i];
+                     b =  Blue[i];
+                     for (j=0; j<dc; j++) {
+                         d = abs(r - (ctab[j].red>>8)) +
+                             abs(g - (ctab[j].green>>8)) +
+                             abs(b - (ctab[j].blue>>8));
+                         if (d<mdist) { mdist=d; close=j; }
+                         }
+                     if (close<0) FatalError("simply can't do it.  Sorry.");
+                     bcopy(&defs[close],&defs[i],sizeof(XColor));
+                     cols[i] = ctab[close].pixel;
+                     }
+             }	/* end 'failed to pull it off' */
+         }
+ 
+     else {          /* strip wasn't set, do the best auto-strip */
+         j = 0;
+         while (strip<8) {
+             lmask = lmasks[strip];
+             for (i=0; i<numcols; i++)
+                 if (used[i]) {
+                     defs[i].red   = (Red[i]  &lmask)<<8;
+                     defs[i].green = (Green[i]&lmask)<<8;
+                     defs[i].blue  = (Blue[i] &lmask)<<8;
+                     defs[i].flags = DoRed | DoGreen | DoBlue;
+                     if (!XAllocColor(theDisp,theCmap,&defs[i])) break;
+                     cols[i] = defs[i].pixel;
+                     }
+ 
+             if (i<numcols) {		/* failed */
+                 strip++;  j++;
+                 for (i--; i>=0; i--)
+                     if (used[i]) XFreeColors(theDisp,theCmap,cols+i,1,0L);
+                 }
+             else break;
+             }
+ 
+         if (j && strip<8)
+             fprintf(stderr,"%s:  %s stripped %d bits\n",cmd,fname,strip);
+ 
+         if (strip==8) {
+             fprintf(stderr,"UTTERLY failed to allocate the desired colors.\n");
+             for (i=0; i<numcols; i++) cols[i]=i;
+             }
+         }
+ 
+     ptr = Image;
+     for (i=0; i<Height; i++)
+         for (j=0; j<Width; j++,ptr++) 
+             *ptr = (byte) cols[*ptr];
  }
-- 
Mike Wexler(wyse!mikew)    Phone: (408)433-1000 x1330
Moderator of comp.sources.x