浏览代码

don't encode alpha channel twice when alpha is different colorspace from other channels

Sean Barrett 11 年之前
父节点
当前提交
dd28033b34
共有 2 个文件被更改,包括 91 次插入25 次删除
  1. 79 17
      stb_image_resize.h
  2. 12 8
      tests/resample_test.cpp

+ 79 - 17
stb_image_resize.h

@@ -3,8 +3,8 @@
    http://github.com/nothings/stb
 
    Written with emphasis on usability, portability, and efficiency. (No
-   SIMD or threads, so it will not be the fastest implementation around.)
-   Only scaling is supported, no rotations or translations.
+   SIMD or threads, so it be easily outperformed by libs that use those.)
+   Only scaling and translation is supported, no rotations or shears.
 
    COMPILING & LINKING
       In one C/C++ file that #includes this file, do this:
@@ -43,6 +43,11 @@
       ASSERT
          Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
 
+      OPTIMIZATION
+         Define STBIR_SATURATE_INT to compute clamp values in-range using
+         integer operations instead of float operations. This may be faster
+         on some platforms.
+
       DEFAULT FILTERS
          For functions which don't provide explicit control over what filters
          to use, you can change the compile-time defaults with
@@ -78,6 +83,10 @@
                printf("Progress: %f%%\n", progress*100);
             }
 
+      MAX CHANNELS
+         If your image has more than 64 channels, define STBIR_MAX_CHANNELS
+         to the max you'll have.
+
       ALPHA CHANNEL
          Most of the resizing functions provide the ability to control how
          the alpha channel of an image is processed. The important things
@@ -419,6 +428,15 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]
 #define STBIR_PROGRESS_REPORT(float_0_to_1)
 #endif
 
+#ifndef STBIR_MAX_CHANNELS
+#define STBIR_MAX_CHANNELS 64
+#endif
+
+#if STBIR_MAX_CHANNELS > 65536
+#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536."
+// because we store the indices in 16-bit variables
+#endif
+
 // This value is added to alpha just before premultiplication to avoid
 // zeroing out color values. It is equivalent to 2^-80. If you don't want
 // that behavior (it may interfere if you have floating point images with
@@ -551,6 +569,30 @@ static stbir__inline float stbir__saturate(float x)
     return x;
 }
 
+#ifdef STBIR_SATURATE_INT
+static stbir__inline stbir_uint8 stbir__saturate8(int x)
+{
+    if ((unsigned int) x <= 255)
+        return x;
+
+    if (x < 0)
+        return 0;
+
+    return 255;
+}
+
+static stbir__inline stbir_uint16 stbir__saturate16(int x)
+{
+    if ((unsigned int) x <= 65535)
+        return x;
+
+    if (x < 0)
+        return 0;
+
+    return 65535;
+}
+#endif
+
 static float stbir__srgb_uchar_to_linear_float[256] = {
     0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f,
     0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f,
@@ -1594,6 +1636,8 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
 {
     int x;
     int n;
+    int num_nonalpha;
+    stbir_uint16 nonalpha[STBIR_MAX_CHANNELS];
 
     if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
     {
@@ -1603,19 +1647,36 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
 
             float alpha = encode_buffer[pixel_index + alpha_channel];
             float reciprocal_alpha = alpha ? 1.0f / alpha : 0;
+
+            // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb
             for (n = 0; n < channels; n++)
                 if (n != alpha_channel)
                     encode_buffer[pixel_index + n] *= reciprocal_alpha;
 
             // We added in a small epsilon to prevent the color channel from being deleted with zero alpha.
             // Because we only add it for integer types, it will automatically be discarded on integer
-            // conversion.
+            // conversion, so we don't need to subtract it back out (which would be problematic for
+            // numeric precision reasons).
         }
     }
 
+    // build a table of all channels that need colorspace correction, so
+    // we don't perform colorspace correction on channels that don't need it.
+    for (x=0, num_nonalpha=0; x < channels; ++x)
+        if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
+            nonalpha[num_nonalpha++] = x;
+
     #define STBIR__ROUND_INT(f)    ((int)          ((f)+0.5))
     #define STBIR__ROUND_UINT(f)   ((stbir_uint32) ((f)+0.5))
 
+    #ifdef STBIR__SATURATE_INT
+    #define STBIR__ENCODE_LINEAR8(f)   stbir__saturate8 (STBIR__ROUND_INT((f) * 255  ))
+    #define STBIR__ENCODE_LINEAR16(f)  stbir__saturate16(STBIR__ROUND_INT((f) * 65535))
+    #else
+    #define STBIR__ENCODE_LINEAR8(f)   (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * 255  )
+    #define STBIR__ENCODE_LINEAR16(f)  (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * 65535)
+    #endif
+
     switch (decode)
     {
         case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR):
@@ -1626,7 +1687,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
                 for (n = 0; n < channels; n++)
                 {
                     int index = pixel_index + n;
-                    ((unsigned char*)output_buffer)[index] = (unsigned char) STBIR__ROUND_INT(stbir__saturate(encode_buffer[index]) * 255);
+                    ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]);
                 }
             }
             break;
@@ -1636,14 +1697,14 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
             {
                 int pixel_index = x*channels;
 
-                for (n = 0; n < channels; n++)
+                for (n = 0; n < num_nonalpha; n++)
                 {
-                    int index = pixel_index + n;
+                    int index = pixel_index + nonalpha[n];
                     ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]);
                 }
 
-                if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
-                    ((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char) STBIR__ROUND_INT(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255);
+                if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
+                    ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]);
             }
             break;
 
@@ -1655,7 +1716,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
                 for (n = 0; n < channels; n++)
                 {
                     int index = pixel_index + n;
-                    ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__saturate(encode_buffer[index]) * 65535);
+                    ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]);
                 }
             }
             break;
@@ -1665,14 +1726,14 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
             {
                 int pixel_index = x*channels;
 
-                for (n = 0; n < channels; n++)
+                for (n = 0; n < num_nonalpha; n++)
                 {
-                    int index = pixel_index + n;
+                    int index = pixel_index + nonalpha[n];
                     ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535);
                 }
 
                 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
-                    ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)STBIR__ROUND_INT(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535);
+                    ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]);
             }
 
             break;
@@ -1695,9 +1756,9 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
             {
                 int pixel_index = x*channels;
 
-                for (n = 0; n < channels; n++)
+                for (n = 0; n < num_nonalpha; n++)
                 {
-                    int index = pixel_index + n;
+                    int index = pixel_index + nonalpha[n];
                     ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295);
                 }
 
@@ -1724,9 +1785,9 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
             {
                 int pixel_index = x*channels;
 
-                for (n = 0; n < channels; n++)
+                for (n = 0; n < num_nonalpha; n++)
                 {
-                    int index = pixel_index + n;
+                    int index = pixel_index + nonalpha[n];
                     ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]);
                 }
 
@@ -2186,8 +2247,9 @@ static int stbir__resize_allocated(stbir__info *info,
 #endif
 
     STBIR_ASSERT(info->channels >= 0);
+    STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS);
 
-    if (info->channels < 0)
+    if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS)
         return 0;
 
     STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));

+ 12 - 8
tests/resample_test.cpp

@@ -148,25 +148,26 @@ static void performance(int argc, char **argv)
 	int w, h, count;
 	int n, i;
 	int out_w, out_h, srgb=1;
-	input_pixels = stbi_load(argv[1], &w, &h, &n, 0);
-    #if 1
+	input_pixels = stbi_load(argv[1], &w, &h, &n, 4);
+    n=4;
+    #if 0
     out_w = w/4; out_h = h/4; count=100; // 1
     #elif 0
 	out_w = w*2; out_h = h/4; count=20; // 2   // note this is structured pessimily, would be much faster to downsample vertically first
     #elif 0
     out_w = w/4; out_h = h*2; count=50; // 3
     #elif 0
-    out_w = w*3; out_h = h*3; count=5; srgb=0; // 4
+    out_w = w*3; out_h = h*3; count=2; srgb=0; // 4
     #else
-    out_w = w*3; out_h = h*3; count=3; // 5   // this is dominated by linear->sRGB conversion
+    out_w = w*3; out_h = h*3; count=1; // 5   // this is dominated by linear->sRGB conversion
     #endif
 
 	output_pixels = (unsigned char*) malloc(out_w*out_h*n);
     for (i=0; i < count; ++i)
         if (srgb)
-	        stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0);
+	        stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, 3,0);
         else
-	        stbir_resize_uint8(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n);
+	        stbir_resize(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, STBIR_TYPE_UINT8, n, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_COLORSPACE_LINEAR, NULL);
 	exit(0);
 }
 
@@ -228,6 +229,7 @@ void test_format(const char* file, float width_percent, float height_percent, st
 	int new_h = (int)(h * height_percent);
 
 	T* T_data = (T*)malloc(w * h * n * sizeof(T));
+    memset(T_data, 0, w*h*n*sizeof(T));
 	convert_image<unsigned char, T>(input_data, T_data, w * h * n);
 
 	T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T));
@@ -792,13 +794,15 @@ void test_suite(int argc, char **argv)
 
 	_mkdir("test-output");
 
-    test_32();
-
 	if (argc > 1)
 		barbara = argv[1];
 	else
 		barbara = "barbara.png";
 
+	test_format<unsigned short>(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB);
+    test_32();
+
+
 	// check what cases we need normalization for
 #if 1
 	{