瀏覽代碼

Merge remote-tracking branch 'remotes/nothings/resample' into resample

Jorge Rodriguez 11 年之前
父節點
當前提交
a25c1d2bbd
共有 2 個文件被更改,包括 241 次插入142 次删除
  1. 206 128
      stb_image_resize.h
  2. 35 14
      tests/resample_test.cpp

+ 206 - 128
stb_image_resize.h

@@ -1,37 +1,132 @@
-/* stb_image_resize - v0.50 - public domain image resampling
-   no warranty implied; use at your own risk
+/*  stb_image_resize - v0.90 - public domain image resizing
+    by Jorge L Rodriguez (@VinoBS) - 2014
+	http://github.com/nothings/stb
 
 
-   Do this:
-      #define STB_IMAGE_RESIZE_IMPLEMENTATION
-   before you include this file in *one* C or C++ file to create the implementation.
+    Written with emphasis on usage and speed. Only scaling is
+    currently supported, no rotations or translations.
 
 
-   #define STBIR_ASSERT(x) to avoid using assert.h.
+	DOCUMENTATION
 
 
-   #define STBIR_MALLOC(size,context) and STBIR_FREE(ptr,context) to avoid using stdlib.h malloc.
-      Each function makes exactly one call to malloc/free, so to avoid allocations,
-      pass in a temp memory block as context and return that from MALLOC.
+		COMPILING & LINKING
+			In one C/C++ file that #includes this file, do this:
+				#define STB_IMAGE_RESIZE_IMPLEMENTATION
+			before the #include. That will create the implementation in that file.
 
 
-   QUICK NOTES:
-      Written with emphasis on usage and speed. Only the resize operation is
-          currently supported, no rotations or translations.
+		API
+			See the "header file" section of the source for API documentation.
 
 
-      Supports arbitrary resize for separable filters. For a list of
-          supported filters see the stbir_filter enum. To add a new filter,
-          write a filter function and add it to stbir__filter_info_table.
+		MEMORY ALLOCATION
+			The resize functions here perform a single memory allocation using
+			malloc. To control the memory allocation, before the #include that
+			triggers the implementation, do:
 
 
-   STBIR_MAX_CHANNELS: defaults to 16, if you need more, bump it up
+				#define STBIR_MALLOC(size,context) ...
+				#define STBIR_FREE(ptr,context)    ...
 
 
-   Revisions:
-      0.50 (2014-??-??) first released version
+			Each resize function makes exactly one call to malloc/free, so to use
+			temp memory, store the temp memory in the context and return that.
 
 
-   TODO:
-      Installable filters
-      Specify wrap and filter modes independently for each axis
-      Resize that respects alpha test coverage
-         (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage:
-         https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp )
+		ASSERT
+			Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
 
 
-   Initial implementation by Jorge L Rodriguez, @VinoBS
+		DEFAULT FILTERS
+			For functions which don't provide explicit control over what filters
+			to use, you can change the compile-time defaults with
+
+				#define STBIR_DEFAULT_FILTER_UPSAMPLE     STBIR_FILTER_something
+				#define STBIR_DEFAULT_FILTER_DOWNSAMPLE   STBIR_FILTER_something
+
+			See stbir_filter in the header-file section for the list of filters.
+
+		NEW FILTERS
+			A number of 1D filter kernels are used. For a list of
+			supported filters see the stbir_filter enum. To add a new filter,
+			write a filter function and add it to stbir__filter_info_table.
+
+		PROGRESS
+			For interactive use with slow resize operations, you can install
+			a progress-report callback:
+
+				#define STBIR_PROGRESS_REPORT(val) my_progress_report(val)
+
+			The parameter val is a float which goes from 0 to 1 as progress is made.
+
+			For example:
+
+				void my_progress_report(float progress)
+				{
+					printf("Progress: %f%%\n", progress*100);
+				}
+
+		ALPHA CHANNEL
+			Most of the resizing functions provide the ability to control how
+			the alpha channel of an image is processed. The important things
+			to know about this:
+
+			1. The best mathematically-behaved version of alpha to use is
+			called "premultiplied alpha", in which the other color channels
+			have had the alpha value multiplied in. If you use premultiplied
+			alpha, linear filtering (such as image resampling done by this
+			library, or performed in texture units on GPUs) does the "right
+			thing". While premultiplied alpha is standard in the movie CGI
+			industry, it is still uncommon in the videogame/real-time world.
+
+			If you linearly filter non-premultiplied alpha, strange effects
+			occur. (For example, the average of 1% opaque bright green
+			and 99% opaque black produces 50% transparent dark green when
+			non-premultiplied, whereas premultiplied it produces 50%
+			transparent near-black. The former introduces green energy
+			that doesn't exist in the source.)
+
+			2. Artists should not edit premultiplied-alpha images; artists
+			want non-premultiplied alpha images. Thus, art tools generally output
+			non-premultiplied alpha images.
+
+			3. You will get best results in most cases by converting images
+			to premultiplied alpha before processing them mathematically.
+
+			4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the
+			resizer does not do anything special for the alpha channel;
+			it is resampled identically to other channels.
+
+			5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED,
+			then the resizer weights the contribution of input pixels
+			based on their alpha values, or, equivalently, it multiplies
+			the alpha value into the color channels, resamples, then divides
+			by the resultant alpha value. Input pixels which have alpha=0 do
+			not contribute at all to output pixels unless _all_ of the input
+			pixels affecting that output pixel have alpha=0, in which case
+			the result for that pixel is the same as it would be without
+			STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for
+			input images in integer formats. For input images in float format,
+			input pixels with alpha=0 have no effect, and output pixels
+			which have alpha=0 will be 0 in all channels. (For float images,
+			you can manually achieve the same result by adding a tiny epsilon
+			value to the alpha channel of every image, and then subtracting
+			or clamping it at the end.)
+
+			6. You can separately control whether the alpha channel is
+			interpreted as linear or affected by the colorspace. By default
+			it is linear; you almost never want to apply the colorspace.
+			(For example, graphics hardware does not apply sRGB conversion
+			to the alpha channel.)
+
+	ADDITIONAL CONTRIBUTORS
+	   Sean Barrett: API design, optimizations
+			
+	REVISIONS
+		0.90 (2014-??-??) first released version
+
+	LICENSE
+		This software is in the public domain. Where that dedication is not
+		recognized, you are granted a perpetual, irrevocable license to copy
+		and modify this file as you see fit.
+
+	TODO
+		Installable filters
+		Resize that respects alpha test coverage
+			(Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage:
+			https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp )
 */
 */
 
 
 #ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H
 #ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H
@@ -95,9 +190,9 @@ STBIRDEF int stbir_resize_float(     const float *input_pixels , int input_w , i
 #define STBIR_ALPHA_CHANNEL_NONE       -1
 #define STBIR_ALPHA_CHANNEL_NONE       -1
 
 
 // Set this flag if your texture has premultiplied alpha. Otherwise, stbir will
 // Set this flag if your texture has premultiplied alpha. Otherwise, stbir will
-// use alpha-correct resampling by multiplying the the specified alpha channel
-// into all other channels before resampling, then dividing back out after.
-#define STBIR_FLAG_PREMULTIPLIED_ALPHA    (1 << 0)
+// use alpha-weighted resampling (effectively premultiplying, resampling,
+// then unpremultiplying).
+#define STBIR_FLAG_ALPHA_PREMULTIPLIED    (1 << 0)
 // The specified alpha channel should be handled as gamma-corrected value even
 // The specified alpha channel should be handled as gamma-corrected value even
 // when doing sRGB operations.
 // when doing sRGB operations.
 #define STBIR_FLAG_ALPHA_USES_COLORSPACE  (1 << 1)
 #define STBIR_FLAG_ALPHA_USES_COLORSPACE  (1 << 1)
@@ -130,7 +225,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels
 //     * Alpha-channel can be processed separately
 //     * Alpha-channel can be processed separately
 //       * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE
 //       * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE
 //         * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT)
 //         * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT)
-//         * Filters can be weighted by alpha channel (if flags&STBIR_FLAG_NONPREMUL_ALPHA)
+//         * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)
 //     * Filter can be selected explicitly
 //     * Filter can be selected explicitly
 //     * uint16 image type
 //     * uint16 image type
 //     * sRGB colorspace available for all types
 //     * sRGB colorspace available for all types
@@ -227,29 +322,6 @@ STBIRDEF int stbir_resize_region(  const void *input_pixels , int input_w , int
                                    float s0, float t0, float s1, float t1);
                                    float s0, float t0, float s1, float t1);
 // (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use.
 // (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use.
 
 
-
-// Define this if you want a progress report.
-// Example:
-// void my_progress_report(float progress)
-// {
-//     printf("Progress: %f%%\n", progress*100);
-// }
-//
-// #define STBIR_PROGRESS_REPORT my_progress_report
-
-#ifndef STBIR_PROGRESS_REPORT
-#define STBIR_PROGRESS_REPORT(float_0_to_1)
-#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
-// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to
-// disable it.
-#ifndef STBIR_EPSILON
-#define STBIR_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20))
-#endif
-
 //
 //
 //
 //
 ////   end header file   /////////////////////////////////////////////////////
 ////   end header file   /////////////////////////////////////////////////////
@@ -282,9 +354,8 @@ STBIRDEF int stbir_resize_region(  const void *input_pixels , int input_w , int
 
 
 #ifndef STBIR_MALLOC
 #ifndef STBIR_MALLOC
 #include <stdlib.h>
 #include <stdlib.h>
-
-#define STBIR_MALLOC(x,c) malloc(x)
-#define STBIR_FREE(x,c)   free(x)
+#define STBIR_MALLOC(size,c) malloc(size)
+#define STBIR_FREE(ptr,c)   free(ptr)
 #endif
 #endif
 
 
 #ifndef _MSC_VER
 #ifndef _MSC_VER
@@ -317,10 +388,21 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]
 #define STBIR_DEFAULT_FILTER_DOWNSAMPLE  STBIR_FILTER_MITCHELL
 #define STBIR_DEFAULT_FILTER_DOWNSAMPLE  STBIR_FILTER_MITCHELL
 #endif
 #endif
 
 
-#ifndef STBIR_MAX_CHANNELS
-#define STBIR_MAX_CHANNELS  16
+#ifndef STBIR_PROGRESS_REPORT
+#define STBIR_PROGRESS_REPORT(float_0_to_1)
+#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
+// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to
+// disable it.
+#ifndef STBIR_ALPHA_EPSILON
+#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20))
 #endif
 #endif
 
 
+
+
 #define STBIR__UNUSED_PARAM(s) s=s;
 #define STBIR__UNUSED_PARAM(s) s=s;
 
 
 // must match stbir_datatype
 // must match stbir_datatype
@@ -457,38 +539,38 @@ static float stbir__srgb_uchar_to_linear_float[256] = {
 // sRGB transition values, scaled by 1<<28
 // sRGB transition values, scaled by 1<<28
 static int stbir__srgb_offset_to_linear_scaled[256] =
 static int stbir__srgb_offset_to_linear_scaled[256] =
 {
 {
-	    40579,    121738,    202897,    284056,    365216,    446375,    527534,    608693,
-	   689852,    771011,    852421,    938035,   1028466,   1123787,   1224073,   1329393,
-	  1439819,   1555418,   1676257,   1802402,   1933917,   2070867,   2213313,   2361317,
-	  2514938,   2674237,   2839271,   3010099,   3186776,   3369359,   3557903,   3752463,
-	  3953090,   4159840,   4372764,   4591913,   4817339,   5049091,   5287220,   5531775,
-	  5782804,   6040356,   6304477,   6575216,   6852618,   7136729,   7427596,   7725263,
-	  8029775,   8341176,   8659511,   8984821,   9317151,   9656544,  10003040,  10356683,
-	 10717513,  11085572,  11460901,  11843540,  12233529,  12630908,  13035717,  13447994,
-	 13867779,  14295110,  14730025,  15172563,  15622760,  16080655,  16546285,  17019686,
-	 17500894,  17989948,  18486882,  18991734,  19504536,  20025326,  20554138,  21091010,
-	 21635972,  22189062,  22750312,  23319758,  23897432,  24483368,  25077600,  25680162,
-	 26291086,  26910406,  27538152,  28174360,  28819058,  29472282,  30134062,  30804430,
-	 31483418,  32171058,  32867378,  33572412,  34286192,  35008744,  35740104,  36480296,
-	 37229356,  37987316,  38754196,  39530036,  40314860,  41108700,  41911584,  42723540,
-	 43544600,  44374792,  45214140,  46062680,  46920440,  47787444,  48663720,  49549300,
-	 50444212,  51348480,  52262136,  53185204,  54117712,  55059688,  56011160,  56972156,
-	 57942704,  58922824,  59912552,  60911908,  61920920,  62939616,  63968024,  65006168,
-	 66054072,  67111760,  68179272,  69256616,  70343832,  71440936,  72547952,  73664920,
-	 74791848,  75928776,  77075720,  78232704,  79399760,  80576904,  81764168,  82961576,
-	 84169152,  85386920,  86614904,  87853120,  89101608,  90360384,  91629480,  92908904,
-	 94198688,  95498864,  96809440,  98130456,  99461928, 100803872, 102156320, 103519296,
-	104892824, 106276920, 107671616, 109076928, 110492880, 111919504, 113356808, 114804824,
-	116263576, 117733080, 119213360, 120704448, 122206352, 123719104, 125242720, 126777232,
-	128322648, 129879000, 131446312, 133024600, 134613888, 136214192, 137825552, 139447968,
-	141081456, 142726080, 144381808, 146048704, 147726768, 149416016, 151116496, 152828192,
-	154551168, 156285408, 158030944, 159787808, 161556000, 163335568, 165126512, 166928864,
-	168742640, 170567856, 172404544, 174252704, 176112384, 177983568, 179866320, 181760640,
-	183666528, 185584032, 187513168, 189453952, 191406400, 193370544, 195346384, 197333952,
-	199333264, 201344352, 203367216, 205401904, 207448400, 209506752, 211576960, 213659056,
-	215753056, 217858976, 219976832, 222106656, 224248464, 226402272, 228568096, 230745952,
-	232935872, 235137872, 237351968, 239578176, 241816512, 244066992, 246329648, 248604512,
-	250891568, 253190848, 255502368, 257826160, 260162240, 262510608, 264871312, 267244336,
+	        0,     40738,    122216,    203693,    285170,    366648,    448125,    529603,
+	   611080,    692557,    774035,    855852,    942009,   1033024,   1128971,   1229926,
+	  1335959,   1447142,   1563542,   1685229,   1812268,   1944725,   2082664,   2226148,
+	  2375238,   2529996,   2690481,   2856753,   3028870,   3206888,   3390865,   3580856,
+	  3776916,   3979100,   4187460,   4402049,   4622919,   4850123,   5083710,   5323731,
+	  5570236,   5823273,   6082892,   6349140,   6622065,   6901714,   7188133,   7481369,
+	  7781466,   8088471,   8402427,   8723380,   9051372,   9386448,   9728650,  10078021,
+	 10434603,  10798439,  11169569,  11548036,  11933879,  12327139,  12727857,  13136073,
+	 13551826,  13975156,  14406100,  14844697,  15290987,  15745007,  16206795,  16676389,
+	 17153826,  17639142,  18132374,  18633560,  19142734,  19659934,  20185196,  20718552,
+	 21260042,  21809696,  22367554,  22933648,  23508010,  24090680,  24681686,  25281066,
+	 25888850,  26505076,  27129772,  27762974,  28404716,  29055026,  29713942,  30381490,
+	 31057708,  31742624,  32436272,  33138682,  33849884,  34569912,  35298800,  36036568,
+	 36783260,  37538896,  38303512,  39077136,  39859796,  40651528,  41452360,  42262316,
+	 43081432,  43909732,  44747252,  45594016,  46450052,  47315392,  48190064,  49074096,
+	 49967516,  50870356,  51782636,  52704392,  53635648,  54576432,  55526772,  56486700,
+	 57456236,  58435408,  59424248,  60422780,  61431036,  62449032,  63476804,  64514376,
+	 65561776,  66619028,  67686160,  68763192,  69850160,  70947088,  72053992,  73170912,
+	 74297864,  75434880,  76581976,  77739184,  78906536,  80084040,  81271736,  82469648,
+	 83677792,  84896192,  86124888,  87363888,  88613232,  89872928,  91143016,  92423512,
+	 93714432,  95015816,  96327688,  97650056,  98982952, 100326408, 101680440, 103045072,
+	104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544,
+	115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832,
+	127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528,
+	140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968,
+	154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184,
+	168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992,
+	183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968,
+	199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480,
+	215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656,
+	232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464,
+	250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664,
 };
 };
 
 
 static float stbir__srgb_to_linear(float f)
 static float stbir__srgb_to_linear(float f)
@@ -513,32 +595,25 @@ static unsigned char stbir__linear_to_srgb_uchar(float f)
 	int v = 0;
 	int v = 0;
 	int i;
 	int i;
 
 
-	// Everything below 128 is off by 1. This fixes that.
-	int fix = 0;
-
-	// Adding 1 to 0 with the fix gives incorrect results for input 0. This fixes that.
-	if (x < 81000)
-		return 0;
-
-	i =    128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; else fix = 1;
-	i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
-	i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
-	i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
-	i = v + 8;  if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
-	i = v + 4;  if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
-	i = v + 2;  if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
-	i = v + 1;  if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+	i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+	i = v +  64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+	i = v +  32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+	i = v +  16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+	i = v +   8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+	i = v +   4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+	i = v +   2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
+	i = v +   1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
 
 
-	return (unsigned char)v + fix;
+	return (unsigned char) v;
 }
 }
 
 
 static float stbir__filter_trapezoid(float x, float scale)
 static float stbir__filter_trapezoid(float x, float scale)
 {
 {
-	STBIR__DEBUG_ASSERT(scale <= 1);
-	x = (float)fabs(x);
-
 	float halfscale = scale / 2;
 	float halfscale = scale / 2;
 	float t = 0.5f + halfscale;
 	float t = 0.5f + halfscale;
+	STBIR__DEBUG_ASSERT(scale <= 1);
+
+	x = (float)fabs(x);
 
 
 	if (x >= t)
 	if (x >= t)
 		return 0;
 		return 0;
@@ -917,6 +992,7 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info)
 
 
 	for (i = 0; i < stbir_info->output_w; i++)
 	for (i = 0; i < stbir_info->output_w; i++)
 	{
 	{
+		float scale;
 		float total = 0;
 		float total = 0;
 
 
 		for (j = 0; j < num_contributors; j++)
 		for (j = 0; j < num_contributors; j++)
@@ -933,7 +1009,7 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info)
 		STBIR__DEBUG_ASSERT(total > 0.9f);
 		STBIR__DEBUG_ASSERT(total > 0.9f);
 		STBIR__DEBUG_ASSERT(total < 1.1f);
 		STBIR__DEBUG_ASSERT(total < 1.1f);
 
 
-		float scale = 1 / total;
+		scale = 1 / total;
 
 
 		for (j = 0; j < num_contributors; j++)
 		for (j = 0; j < num_contributors; j++)
 		{
 		{
@@ -1168,14 +1244,18 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
 	}
 	}
 
 
 #ifndef STBIR_NO_ALPHA_EPSILON
 #ifndef STBIR_NO_ALPHA_EPSILON
-	if (!(stbir_info->flags & STBIR_FLAG_PREMULTIPLIED_ALPHA))
+	if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED))
 	{
 	{
 		for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++)
 		for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++)
 		{
 		{
 			int decode_pixel_index = x * channels;
 			int decode_pixel_index = x * channels;
 
 
 			// If the alpha value is 0 it will clobber the color values. Make sure it's not.
 			// If the alpha value is 0 it will clobber the color values. Make sure it's not.
-			float alpha = (decode_buffer[decode_pixel_index + alpha_channel] += STBIR_EPSILON);
+			float alpha = decode_buffer[decode_pixel_index + alpha_channel];
+			if (stbir_info->type != STBIR_TYPE_FLOAT) {
+				alpha += STBIR_ALPHA_EPSILON;
+				decode_buffer[decode_pixel_index + alpha_channel] = alpha;
+			}
 
 
 			for (c = 0; c < channels; c++)
 			for (c = 0; c < channels; c++)
 			{
 			{
@@ -1351,28 +1431,27 @@ static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buff
 }
 }
 
 
 
 
-// @OPTIMIZE: embed stbir__encode_pixel and move switch out of per-pixel loop
 static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode)
 static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode)
 {
 {
 	int x;
 	int x;
 	int n;
 	int n;
 
 
 #ifndef STBIR_NO_ALPHA_EPSILON
 #ifndef STBIR_NO_ALPHA_EPSILON
-	if (!(stbir_info->flags&STBIR_FLAG_PREMULTIPLIED_ALPHA))
+	if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
 	{
 	{
 		for (x=0; x < num_pixels; ++x)
 		for (x=0; x < num_pixels; ++x)
 		{
 		{
 			int pixel_index = x*channels;
 			int pixel_index = x*channels;
 
 
 			float alpha = encode_buffer[pixel_index + alpha_channel];
 			float alpha = encode_buffer[pixel_index + alpha_channel];
-			STBIR__DEBUG_ASSERT(alpha > 0);
 			float reciprocal_alpha = alpha ? 1.0f / alpha : 0;
 			float reciprocal_alpha = alpha ? 1.0f / alpha : 0;
 			for (n = 0; n < channels; n++)
 			for (n = 0; n < channels; n++)
 				if (n != alpha_channel)
 				if (n != alpha_channel)
 					encode_buffer[pixel_index + n] *= reciprocal_alpha;
 					encode_buffer[pixel_index + n] *= reciprocal_alpha;
 
 
-			// We added in a small epsilon to prevent the color channel from being deleted with zero alpha. Remove it now.
-			encode_buffer[pixel_index + alpha_channel] -= STBIR_EPSILON;
+			// 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.
 		}
 		}
 	}
 	}
 #endif
 #endif
@@ -1387,7 +1466,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
 			for (n = 0; n < channels; n++)
 			for (n = 0; n < channels; n++)
 			{
 			{
 				int index = pixel_index + n;
 				int index = pixel_index + n;
-				((unsigned char*)output_buffer)[index] = (unsigned char)(round(stbir__saturate(encode_buffer[index]) * 255));
+				((unsigned char*)output_buffer)[index] = (unsigned char)(floor(stbir__saturate(encode_buffer[index]) * 255 + 0.5f));
 			}
 			}
 		}
 		}
 		break;
 		break;
@@ -1404,7 +1483,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
 			}
 			}
 
 
 			if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
 			if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
-				((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char)(round(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255));
+				((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255+0.5f));
 		}
 		}
 		break;
 		break;
 
 
@@ -1416,7 +1495,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
 			for (n = 0; n < channels; n++)
 			for (n = 0; n < channels; n++)
 			{
 			{
 				int index = pixel_index + n;
 				int index = pixel_index + n;
-				((unsigned short*)output_buffer)[index] = (unsigned short)(round(stbir__saturate(encode_buffer[index]) * 65535));
+				((unsigned short*)output_buffer)[index] = (unsigned short)(floor(stbir__saturate(encode_buffer[index]) * 65535+0.5f));
 			}
 			}
 		}
 		}
 		break;
 		break;
@@ -1429,11 +1508,11 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
 			for (n = 0; n < channels; n++)
 			for (n = 0; n < channels; n++)
 			{
 			{
 				int index = pixel_index + n;
 				int index = pixel_index + n;
-				((unsigned short*)output_buffer)[index] = (unsigned short)(round(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535));
+				((unsigned short*)output_buffer)[index] = (unsigned short)(floor(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535 + 0.5f));
 			}
 			}
 
 
 			if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
 			if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
-				((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)(round(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535));
+				((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535 + 0.5f));
 		}
 		}
 
 
 		break;
 		break;
@@ -1446,7 +1525,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
 			for (n = 0; n < channels; n++)
 			for (n = 0; n < channels; n++)
 			{
 			{
 				int index = pixel_index + n;
 				int index = pixel_index + n;
-				((unsigned int*)output_buffer)[index] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[index])) * 4294967295));
+				((unsigned int*)output_buffer)[index] = (unsigned int)(floor(((double)stbir__saturate(encode_buffer[index])) * 4294967295 + 0.5f));
 			}
 			}
 		}
 		}
 		break;
 		break;
@@ -1459,11 +1538,11 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
 			for (n = 0; n < channels; n++)
 			for (n = 0; n < channels; n++)
 			{
 			{
 				int index = pixel_index + n;
 				int index = pixel_index + n;
-				((unsigned int*)output_buffer)[index] = (unsigned int)(round(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295));
+				((unsigned int*)output_buffer)[index] = (unsigned int)(floor(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295 + 0.5f));
 			}
 			}
 
 
 			if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
 			if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
-				((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295));
+				((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)(floor(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295 + 0.5f));
 		}
 		}
 		break;
 		break;
 
 
@@ -1848,10 +1927,9 @@ static int stbir__resize_allocated(stbir__info *info,
 	memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE);
 	memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE);
 #endif
 #endif
 
 
-	STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS);
 	STBIR_ASSERT(info->channels >= 0);
 	STBIR_ASSERT(info->channels >= 0);
 
 
-	if (info->channels > STBIR_MAX_CHANNELS || info->channels < 0)
+	if (info->channels < 0)
 		return 0;
 		return 0;
 
 
 	STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
 	STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
@@ -1863,9 +1941,9 @@ static int stbir__resize_allocated(stbir__info *info,
 		return 0;
 		return 0;
 
 
 	if (alpha_channel < 0)
 	if (alpha_channel < 0)
-		flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_PREMULTIPLIED_ALPHA;
+		flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED;
 
 
-	if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_PREMULTIPLIED_ALPHA))
+	if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
 		STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels);
 		STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels);
 
 
 	if (alpha_channel >= info->channels)
 	if (alpha_channel >= info->channels)

+ 35 - 14
tests/resample_test.cpp

@@ -45,7 +45,7 @@ void* stbir_malloc(size_t size, void* context)
 void stbir_free(void* memory, void* context)
 void stbir_free(void* memory, void* context)
 {
 {
 	if (!context)
 	if (!context)
-		return free(memory);
+		free(memory);
 }
 }
 
 
 //#include <stdio.h>
 //#include <stdio.h>
@@ -425,10 +425,10 @@ void test_premul()
 	float ga = (float)25 / 4294967296;
 	float ga = (float)25 / 4294967296;
 	float a = (ra + ga) / 2;
 	float a = (ra + ga) / 2;
 
 
-	STBIR_ASSERT(output[0] == (int)(r * ra / 2 / a * 4294967296 + 0.5f)); // 232
-	STBIR_ASSERT(output[1] == (int)(g * ga / 2 / a * 4294967296 + 0.5f)); // 23
+	STBIR_ASSERT(output[0] == (unsigned int)(r * ra / 2 / a * 4294967296 + 0.5f)); // 232
+	STBIR_ASSERT(output[1] == (unsigned int)(g * ga / 2 / a * 4294967296 + 0.5f)); // 23
 	STBIR_ASSERT(output[2] == 0);
 	STBIR_ASSERT(output[2] == 0);
-	STBIR_ASSERT(output[3] == (int)(a * 4294967296 + 0.5f)); // 140
+	STBIR_ASSERT(output[3] == (unsigned int)(a * 4294967296 + 0.5f)); // 140
 
 
 	// Now a test to make sure it doesn't clobber existing values.
 	// Now a test to make sure it doesn't clobber existing values.
 
 
@@ -606,7 +606,7 @@ void verify_box(void)
 	STBIR_ASSERT(output11[0][0] == ((t+32)>>6));
 	STBIR_ASSERT(output11[0][0] == ((t+32)>>6));
 }
 }
 
 
-void verify_filter_normalized(stbir_filter filter, int output_size, int value)
+void verify_filter_normalized(stbir_filter filter, int output_size, unsigned int value)
 {
 {
 	int i, j;
 	int i, j;
 	unsigned int output[64];
 	unsigned int output[64];
@@ -618,6 +618,11 @@ void verify_filter_normalized(stbir_filter filter, int output_size, int value)
 			STBIR_ASSERT(value == output[j*output_size + i]);
 			STBIR_ASSERT(value == output[j*output_size + i]);
 }
 }
 
 
+float round2(float f)
+{
+	return (float) floor(f+0.5f); // round() isn't C standard pre-C99
+}
+
 void test_filters(void)
 void test_filters(void)
 {
 {
 	int i,j;
 	int i,j;
@@ -673,7 +678,7 @@ void test_filters(void)
 
 
 	{
 	{
 		// This test is designed to produce coefficients that are very badly denormalized.
 		// This test is designed to produce coefficients that are very badly denormalized.
-		int v = 556;
+		unsigned int v = 556;
 
 
 		unsigned int input[100 * 100];
 		unsigned int input[100 * 100];
 		unsigned int output[11 * 11];
 		unsigned int output[11 * 11];
@@ -698,13 +703,13 @@ void test_filters(void)
 
 
 		stbir_resize(input, 3, 1, 0, output, 2, 1, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL);
 		stbir_resize(input, 3, 1, 0, output, 2, 1, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL);
 
 
-		STBIR_ASSERT(output[0] == (int)round((float)(input[0] * 2 + input[1]) / 3));
-		STBIR_ASSERT(output[1] == (int)round((float)(input[2] * 2 + input[1]) / 3));
+		STBIR_ASSERT(output[0] == (unsigned int)round2((float)(input[0] * 2 + input[1]) / 3));
+		STBIR_ASSERT(output[1] == (unsigned int)round2((float)(input[2] * 2 + input[1]) / 3));
 
 
 		stbir_resize(input, 1, 3, 0, output, 1, 2, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL);
 		stbir_resize(input, 1, 3, 0, output, 1, 2, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL);
 
 
-		STBIR_ASSERT(output[0] == (int)round((float)(input[0] * 2 + input[1]) / 3));
-		STBIR_ASSERT(output[1] == (int)round((float)(input[2] * 2 + input[1]) / 3));
+		STBIR_ASSERT(output[0] == (unsigned int)round2((float)(input[0] * 2 + input[1]) / 3));
+		STBIR_ASSERT(output[1] == (unsigned int)round2((float)(input[2] * 2 + input[1]) / 3));
 	}
 	}
 
 
 	{
 	{
@@ -767,6 +772,7 @@ void test_suite(int argc, char **argv)
 	else
 	else
 		barbara = "barbara.png";
 		barbara = "barbara.png";
 
 
+	// check what cases we need normalization for
 #if 1
 #if 1
 	{
 	{
 		float x, y;
 		float x, y;
@@ -804,18 +810,33 @@ void test_suite(int argc, char **argv)
 	}
 	}
 #endif
 #endif
 
 
-	for (i = 0; i < 256; i++)
-		STBIR_ASSERT(stbir__linear_to_srgb_uchar(stbir__srgb_to_linear(float(i) / 255)) == i);
-
 #if 0 // linear_to_srgb_uchar table
 #if 0 // linear_to_srgb_uchar table
 	for (i=0; i < 256; ++i) {
 	for (i=0; i < 256; ++i) {
-		float f = stbir__srgb_to_linear((i+0.5f)/256.0f);
+		float f = stbir__srgb_to_linear((i-0.5f)/255.0f);
 		printf("%9d, ", (int) ((f) * (1<<28)));
 		printf("%9d, ", (int) ((f) * (1<<28)));
 		if ((i & 7) == 7)
 		if ((i & 7) == 7)
 			printf("\n");
 			printf("\n");
 	}
 	}
 #endif
 #endif
 
 
+	// old tests that hacky fix worked on - test that
+	// every uint8 maps to itself
+	for (i = 0; i < 256; i++) {
+		float f = stbir__srgb_to_linear(float(i) / 255);
+		int n = stbir__linear_to_srgb_uchar(f);
+		STBIR_ASSERT(n == i);
+	}
+
+	// new tests that hacky fix failed for - test that
+	// values adjacent to uint8 round to nearest uint8
+	for (i = 0; i < 256; i++) {
+		for (float y = -0.49f; y <= 0.491f; y += 0.01f) {
+			float f = stbir__srgb_to_linear((i+y) / 255.0f);
+			int n = stbir__linear_to_srgb_uchar(f);
+			STBIR_ASSERT(n == i);
+		}
+	}
+
 	test_filters();
 	test_filters();
 
 
 	test_subpixel_1();
 	test_subpixel_1();