[mapserver-commits] r7154 - trunk/mapserver

svn at osgeo.org svn at osgeo.org
Fri Dec 7 04:28:01 EST 2007


Author: tbonfort
Date: 2007-12-07 04:27:51 -0500 (Fri, 07 Dec 2007)
New Revision: 7154

Modified:
   trunk/mapserver/HISTORY.TXT
   trunk/mapserver/mapgd.c
Log:
speed up forced palette rendering (#2422)

Modified: trunk/mapserver/HISTORY.TXT
===================================================================
--- trunk/mapserver/HISTORY.TXT	2007-12-07 08:58:12 UTC (rev 7153)
+++ trunk/mapserver/HISTORY.TXT	2007-12-07 09:27:51 UTC (rev 7154)
@@ -12,6 +12,7 @@
 
 Current Version (5.1-dev, SVN trunk):
 -------------------------------------
+- Speed up forced palette rendering (#2422)
 
 - WMS GetFeatureInfo: honour FEATURE_COUNT for any INFO_FORMAT and 
   apply the FEATURE_COUNT per layer instead of globally (#2423, #1686)

Modified: trunk/mapserver/mapgd.c
===================================================================
--- trunk/mapserver/mapgd.c	2007-12-07 08:58:12 UTC (rev 7153)
+++ trunk/mapserver/mapgd.c	2007-12-07 09:27:51 UTC (rev 7154)
@@ -3258,69 +3258,158 @@
   
   return MS_SUCCESS;
 }
-
-static int msImageCopyForcePaletteGD(gdImagePtr src, gdImagePtr dst) 
+/*
+ * copy src to dst using dst's palette
+ * src must be a truecolor image
+ * dst must be a paletted image
+ * method is to fine tune the caching used to lookup color values in the palette:
+ *  -0 is the default method, which allocates cache memory when needed
+ *  -1 is a memory conservative method, that uses very little caching but is much
+ *   slower
+ *  -2 is a memory hungry caching method (allocates 32MB on the heap) but is the 
+ *   fastest for large images
+ * 
+ * see bug #2422 for some benchmark timings of these methods
+ */
+static int msImageCopyForcePaletteGD(gdImagePtr src, gdImagePtr dst, int method) 
 {
-  int x, y;
-  int w, h;
-  int c, r, g, b;
-  int R[10], G[10], B[10], C[10];
-  int i, color, nCache = 0, iCache =0, maxCache=10;
-  
-  if(!src || !dst) return MS_FAILURE;
-  if(gdImageSX(src) != gdImageSX(dst) || gdImageSY(src) != gdImageSY(dst)) return MS_FAILURE;
-  if(!gdImageTrueColor(src) || gdImageTrueColor(dst)) return MS_FAILURE; /* 24-bit to 8-bit */
+    int x, y;
+    int w, h;
+    int c, r, g, b,color;
+    if(!src || !dst) return MS_FAILURE;
+    if(gdImageSX(src) != gdImageSX(dst) || gdImageSY(src) != gdImageSY(dst)) return MS_FAILURE;
+    if(!gdImageTrueColor(src) || gdImageTrueColor(dst)) return MS_FAILURE; /* 24-bit to 8-bit */
+    w = gdImageSX(src);
+    h = gdImageSY(src);
+    if(method==0) {
+        /*default caching strategy*/
+        
+        /*pointer to cache indexed by 'red' component*/
+        unsigned short ***cols=(unsigned short***)calloc(256,sizeof(unsigned short**));
+        
+        /*pointer to cache, pointed by red component, indexed by green component*/
+        unsigned short **data=(unsigned short**)calloc(256*256,sizeof(unsigned short*));
+        for(r=0;r<256;r++) {
+            /*populate the cache*/
+            cols[r]=&(data[r*256]);
+        }
 
-  w = gdImageSX(src);
-  h = gdImageSY(src);
+        for (y = 0; (y < h); y++) {
+            for (x = 0; (x < w); x++) {
+                c = gdImageGetPixel(src, x, y);  
+                r =  gdTrueColorGetRed(c);
+                g = gdTrueColorGetGreen(c);
+                b = gdTrueColorGetBlue(c);
+                
+                if(cols[r][g]==NULL) {
+                    /*this is the first time we see this r,g couple. 
+                     *allocate bytes for the blue component*/ 
+                    cols[r][g]=(unsigned short*)calloc(256,sizeof(unsigned short));
+                }
 
-  iCache = 0;
+                if(!cols[r][g][b]) {
+                    /*this r,g,b triplet was never seen before
+                     * compute its index in the palette and cache the result
+                     */
+                    color = gdImageColorClosest(dst, r, g, b);
+                    dst->pixels[y][x] = color;
+                    /*the cache data was calloc'ed to avoid initialization
+                     * store 'index+1' so we are sure the test for cols[r][g][b]
+                     * returns true, i.e that this color was cached*/
+                    cols[r][g][b]=color+1;
+                }
+                else {
+                    /*the cache data was calloc'ed to avoid initialization
+                     * the cache contains 'index+1' so we must subtract
+                     * 1 to get the real color index*/
+                    dst->pixels[y][x] = cols[r][g][b]-1;
+                }
+            }
+        }
+        for(r=0;r<256;r++) 
+            for(g=0;g<256;g++) 
+                if(cols[r][g]) /*only free the row if it was used*/
+                    free(cols[r][g]); 
+        free(data);
+        free(cols);
+    }
+    else if(method==1) {
+        /*memory conservative method, does not allocate mem on the heap*/
+        int R[10], G[10], B[10], C[10];
+        int i, color, nCache = 0, iCache =0, maxCache=10;
 
-  for (y = 0; (y < h); y++) {
-    for (x = 0; (x < w); x++) {
-      c = gdImageGetPixel(src, x, y);
-      r =  gdTrueColorGetRed(c);
-      g = gdTrueColorGetGreen(c);
-      b = gdTrueColorGetBlue(c);
-      color = -1;
+        for (y = 0; (y < h); y++) {
+            for (x = 0; (x < w); x++) {
+                c = gdImageGetPixel(src, x, y);
+                r =  gdTrueColorGetRed(c);
+                g = gdTrueColorGetGreen(c);
+                b = gdTrueColorGetBlue(c);
+                color = -1;
 
-      /* adding a simple cache to keep colors instead of always calling gdImageColorClosest
-         seems to reduce significantly the time passed in this function 
-         spcially with large images (bug 2096)*/
-      for (i=0; i<nCache; i++)
-      {
-          if (R[i] == r)
-          {
-              if (G[i] == g && B[i] == b)
-              {
-                  color = C[i];
-                  break;
-              }
-          }
-      }
+                /* adding a simple cache to keep colors instead of always calling gdImageColorClosest
+               seems to reduce significantly the time passed in this function 
+               spcially with large images (bug 2096)*/
+                for (i=0; i<nCache; i++)
+                {
+                    if (R[i] == r)
+                    {
+                        if (G[i] == g && B[i] == b)
+                        {
+                            color = C[i];
+                            break;
+                        }
+                    }
+                }
 
-      if (color == -1)
-      {
-          color = gdImageColorClosest(dst, r, g, b);
-          R[iCache] = r;
-          G[iCache] = g;
-          B[iCache] = b;
-          C[iCache] = color;
-          nCache++;
-          if (nCache >= maxCache)
-            nCache = maxCache;
-          
-          iCache++;
-          if (iCache == maxCache)
-            iCache = 0;
-      }
+                if (color == -1)
+                {
+                    color = gdImageColorClosest(dst, r, g, b);
+                    R[iCache] = r;
+                    G[iCache] = g;
+                    B[iCache] = b;
+                    C[iCache] = color;
+                    nCache++;
+                    if (nCache >= maxCache)
+                        nCache = maxCache;
 
-      gdImageSetPixel(dst, x, y, color);
+                    iCache++;
+                    if (iCache == maxCache)
+                        iCache = 0;
+                }
+
+                gdImageSetPixel(dst, x, y, color);
+            }
+        }
     }
-  }
-  return MS_SUCCESS;
-} 
+    else if(method==2) {
+        /*memory hungry method, fastest for very large images*/
+        
+        /*use a cache for every possible r,g,b triplet
+         * this is usually a full 32MB (when short is 2 bytes) */
+        short *cache=(short*)calloc(16777216,sizeof(short));
 
+        for (y = 0; (y < h); y++) {
+            for (x = 0; (x < w); x++) {
+                int index;
+                c = gdImageGetPixel(src, x, y);    
+                index=c&0xFFFFFF; /*use r,g,b for the cache index, i.e. remove alpha*/
+                if(!cache[index]) {
+                    r =  gdTrueColorGetRed(c);
+                    g = gdTrueColorGetGreen(c);
+                    b = gdTrueColorGetBlue(c);
+                    color = gdImageColorClosest(dst, r, g, b);
+                    cache[index]=color+1;
+                    dst->pixels[y][x] = color;
+                }
+                else
+                    dst->pixels[y][x] = cache[index]-1;
+            }
+        }
+        free(cache);
+    }
+    return MS_SUCCESS;
+}
+
 static gdImagePtr msImageCreateWithPaletteGD( gdImagePtr img24, const char *palette, int sx, int sy) 
 {
   gdImagePtr img, gdPImgPalette;
@@ -3482,33 +3571,37 @@
 
     if( force_palette ) {
       gdImagePtr gdPImg;
+      int method=0;
       const char *palette = msGetOutputFormatOption( format, "PALETTE", "palette.txt");
-
+      const char *palette_method = msGetOutputFormatOption( format, "PALETTE_MEM", "0");
       gdPImg = msImageCreateWithPaletteGD(img, palette, gdImageSX(img), gdImageSY(img));
+      if(strcasecmp(palette_method,"conservative")==0)
+          method=1;
+      else if(strcasecmp(palette_method,"liberal")==0)
+          method=2;
+      msImageCopyForcePaletteGD(img, gdPImg, method);
 
-      msImageCopyForcePaletteGD(img, gdPImg);
-
-
       gdImagePngCtx(gdPImg, ctx);
       gdImageDestroy(gdPImg);
     } else if( force_pc256 ) {
-      gdImagePtr gdPImg;
-      int dither, i;
-      int colorsWanted = atoi(msGetOutputFormatOption( format, "QUANTIZE_COLORS", "256"));
-      const char *dither_string = msGetOutputFormatOption( format, "QUANTIZE_DITHER", "YES");
+        gdImagePtr gdPImg;
+        int dither, i;
+        int colorsWanted = atoi(msGetOutputFormatOption( format, "QUANTIZE_COLORS", "256"));
+        const char *dither_string = msGetOutputFormatOption( format, "QUANTIZE_DITHER", "YES");
 
-      if( strcasecmp(dither_string,"on") == 0 || strcasecmp(dither_string,"yes") == 0 || strcasecmp(dither_string,"true") == 0 )
-        dither = 1;
-      else
-        dither = 0;
-      
-      gdPImg = gdImageCreatePaletteFromTrueColor(img,dither,colorsWanted);
-      /* It seems there is a bug in gd 2.0.33 and earlier that leaves the 
+        if( strcasecmp(dither_string,"on") == 0 || strcasecmp(dither_string,"yes") == 0 || strcasecmp(dither_string,"true") == 0 )
+            dither = 1;
+        else
+            dither = 0;
+
+        gdPImg = gdImageCreatePaletteFromTrueColor(img,dither,colorsWanted);
+        /* It seems there is a bug in gd 2.0.33 and earlier that leaves the 
          colors open[] flag set to one. */
-      for( i = 0; i < gdPImg->colorsTotal; i++ )
-        gdPImg->open[i] = 0;
-      gdImagePngCtx( gdPImg, ctx );
-      gdImageDestroy( gdPImg );
+        for( i = 0; i < gdPImg->colorsTotal; i++ )
+            gdPImg->open[i] = 0;
+        gdImagePngCtx( gdPImg, ctx );
+        gdImageDestroy( gdPImg );
+
     } else
       gdImagePngCtx( img, ctx );
 #else
@@ -3585,11 +3678,16 @@
 
     if( force_palette ) {
       gdImagePtr gdPImg;
+      int method=0;
       const char *palette = msGetOutputFormatOption( format, "PALETTE", "palette.txt");
+      const char *palette_method = msGetOutputFormatOption( format, "PALETTE_MEM", "0");
+      if(strcasecmp(palette_method,"conservative")==0)
+          method=1;
+      else if(strcasecmp(palette_method,"liberal")==0)
+          method=2;
 
       gdPImg = msImageCreateWithPaletteGD(img, palette, gdImageSX(img), gdImageSY(img));
-
-      msImageCopyForcePaletteGD(img, gdPImg);
+      msImageCopyForcePaletteGD(img, gdPImg, method);
       imgbytes = gdImagePngPtr(gdPImg, size_ptr);
     }
     else if ( force_pc256 ) {



More information about the mapserver-commits mailing list