diff options
author | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2012-01-08 17:55:53 +0400 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2012-01-08 17:55:53 +0400 |
commit | 4487103e61ba1277027d8d4a9105a3f1b29e204c (patch) | |
tree | 7658f4570e1c0ded316ac94458e739f9ffeaa319 /source | |
parent | 3dead22c7322cf72ee63a003ba50940abc12e7c9 (diff) |
Color:
* Accelerated sRGB <=> linear conversion using lookup table, this can speed up
loading of images in the compositor and simple renders quite a bit.
* Dithering now uses the Floyd-Steinberg algorithm. Previously it would simply
randomize each pixel slightly, adding noise, now that should be reduced.
Patch #29309 by David M.
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenlib/BLI_math_color.h | 8 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_utildefines.h | 1 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_color.c | 80 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_color_inline.c | 75 | ||||
-rw-r--r-- | source/blender/imbuf/intern/divers.c | 170 |
5 files changed, 289 insertions, 45 deletions
diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index 7fbb4680b6d..32c300944b4 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -89,6 +89,8 @@ MINLINE void linearrgb_to_srgb_v4(float srgb[4], const float linear[4]); MINLINE void srgb_to_linearrgb_predivide_v4(float linear[4], const float srgb[4]); MINLINE void linearrgb_to_srgb_predivide_v4(float srgb[4], const float linear[4]); +void BLI_init_srgb_conversion(void); + /************************** Other *************************/ int constrain_rgb(float *r, float *g, float *b); @@ -97,13 +99,13 @@ void minmax_rgb(short c[3]); void rgb_float_set_hue_float_offset(float * rgb, float hue_offset); void rgb_byte_set_hue_float_offset(unsigned char * rgb, float hue_offset); +void rgb_byte_to_float(const unsigned char *in, float *out); +void rgb_float_to_byte(const float *in, unsigned char *out); + /***************** lift/gamma/gain / ASC-CDL conversion *****************/ void lift_gamma_gain_to_asc_cdl(float *lift, float *gamma, float *gain, float *offset, float *slope, float *power); -void rgb_byte_to_float(const unsigned char *in, float *out); -void rgb_float_to_byte(const float *in, unsigned char *out); - #ifdef BLI_MATH_INLINE_H #include "intern/math_color_inline.c" #endif diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index abe8fb7d8fe..45049dd6207 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -108,6 +108,7 @@ #define FTOCHAR(val) ((val)<=0.0f)? 0 : (((val)>(1.0f-0.5f/255.0f))? 255 : (char)((255.0f*(val))+0.5f)) #define FTOUSHORT(val) ((val >= 1.0f-0.5f/65535)? 65535: (val <= 0.0f)? 0: (unsigned short)(val*65535.0f + 0.5f)) +#define USHORTTOUCHAR(val) ((unsigned char)(((val)+0x80)>>8)) #define F3TOCHAR3(v2, v1) { \ (v1)[0]= FTOCHAR((v2[0])); \ (v1)[1]= FTOCHAR((v2[1])); \ diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index 8f5366b6317..3627b38a091 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -30,7 +30,11 @@ #include <assert.h> +#include "MEM_guardedalloc.h" + #include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_utildefines.h" void hsv_to_rgb(float h, float s, float v, float *r, float *g, float *b) { @@ -480,3 +484,79 @@ void rgb_byte_set_hue_float_offset(unsigned char rgb[3], float hue_offset) rgb_float_set_hue_float_offset(rgb_float, hue_offset); rgb_float_to_byte(rgb_float, rgb); } + + +/* fast sRGB conversion + * LUT from linear float to 16-bit short + * based on http://mysite.verizon.net/spitzak/conversion/ + */ + +float BLI_color_from_srgb_table[256]; +unsigned short BLI_color_to_srgb_table[0x10000]; + +static unsigned short hipart(const float f) +{ + union { + float f; + unsigned short us[2]; + } tmp; + + tmp.f = f; + +#ifdef __BIG_ENDIAN__ + return tmp.us[0]; +#else + return tmp.us[1]; +#endif +} + +static float index_to_float(const unsigned short i) +{ + union { + float f; + unsigned short us[2]; + } tmp; + + /* positive and negative zeros, and all gradual underflow, turn into zero: */ + if (i<0x80 || (i >= 0x8000 && i < 0x8080)) return 0; + /* All NaN's and infinity turn into the largest possible legal float: */ + if (i>=0x7f80 && i<0x8000) return FLT_MAX; + if (i>=0xff80) return -FLT_MAX; + +#ifdef __BIG_ENDIAN__ + tmp.us[0] = i; + tmp.us[1] = 0x8000; +#else + tmp.us[0] = 0x8000; + tmp.us[1] = i; +#endif + + return tmp.f; +} + +void BLI_init_srgb_conversion(void) +{ + static int initialized= 0; + int i, b; + + if (initialized) return; + initialized = 1; + + /* Fill in the lookup table to convert floats to bytes: */ + for (i = 0; i < 0x10000; i++) { + float f = linearrgb_to_srgb(index_to_float(i))*255.0f; + if (f <= 0) BLI_color_to_srgb_table[i] = 0; + else if (f < 255) BLI_color_to_srgb_table[i] = (unsigned short)(f*0x100+.5); + else BLI_color_to_srgb_table[i] = 0xff00; + } + + /* Fill in the lookup table to convert bytes to float: */ + for (b = 0; b <= 255; b++) { + float f = srgb_to_linearrgb(((float)b)*(1.0f/255.0f)); + BLI_color_from_srgb_table[b] = f; + i = hipart(f); + /* replace entries so byte->float->byte does not change the data: */ + BLI_color_to_srgb_table[i] = b*0x100; + } +} + diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c index aaaa065f14d..1247632cf79 100644 --- a/source/blender/blenlib/intern/math_color_inline.c +++ b/source/blender/blenlib/intern/math_color_inline.c @@ -105,5 +105,80 @@ MINLINE void linearrgb_to_srgb_predivide_v4(float srgb[4], const float linear[4] srgb[3] = linear[3]; } +/* LUT accelerated conversions */ + +extern float BLI_color_from_srgb_table[256]; +extern unsigned short BLI_color_to_srgb_table[0x10000]; + +MINLINE unsigned short to_srgb_table_lookup(const float f) +{ + union { + float f; + unsigned short us[2]; + } tmp; + tmp.f = f; +#ifdef __BIG_ENDIAN__ + return BLI_color_to_srgb_table[tmp.us[0]]; +#else + return BLI_color_to_srgb_table[tmp.us[1]]; +#endif +} + +MINLINE void linearrgb_to_srgb_ushort4(unsigned short srgb[4], const float linear[4]) +{ + srgb[0] = to_srgb_table_lookup(linear[0]); + srgb[1] = to_srgb_table_lookup(linear[1]); + srgb[2] = to_srgb_table_lookup(linear[2]); + srgb[3] = FTOUSHORT(linear[3]); +} + +MINLINE void linearrgb_to_srgb_ushort4_predivide(unsigned short srgb[4], const float linear[4]) +{ + float alpha, inv_alpha, t; + int i; + + if(linear[3] == 1.0f || linear[3] == 0.0f) { + linearrgb_to_srgb_ushort4(srgb, linear); + return; + } + + alpha = linear[3]; + inv_alpha = 1.0f/alpha; + + for(i=0; i<3; ++i) { + t = linear[i] * inv_alpha; + srgb[i] = (t < 1.0f)? to_srgb_table_lookup(t) * alpha : FTOUSHORT(linearrgb_to_srgb(t) * alpha); + } + + srgb[3] = FTOUSHORT(linear[3]); +} + +MINLINE void srgb_to_linearrgb_uchar4(float linear[4], const unsigned char srgb[4]) +{ + linear[0] = BLI_color_from_srgb_table[srgb[0]]; + linear[1] = BLI_color_from_srgb_table[srgb[1]]; + linear[2] = BLI_color_from_srgb_table[srgb[2]]; + linear[3] = srgb[3] * (1.0f/255.0f); +} + +MINLINE void srgb_to_linearrgb_uchar4_predivide(float linear[4], const unsigned char srgb[4]) +{ + float alpha, inv_alpha; + int i; + + if(srgb[3] == 255 || srgb[3] == 0) { + srgb_to_linearrgb_uchar4(linear, srgb); + return; + } + + alpha = srgb[3] * (1.0f/255.0f); + inv_alpha = 1.0f/alpha; + + for(i=0; i<3; ++i) + linear[i] = linearrgb_to_srgb(srgb[i] * inv_alpha) * alpha; + + linear[3] = alpha; +} + #endif /* BLI_MATH_COLOR_INLINE_H */ diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 034b5724ca6..de19f8e6ea6 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -46,9 +46,9 @@ /**************************** Interlace/Deinterlace **************************/ -void IMB_de_interlace(struct ImBuf *ibuf) +void IMB_de_interlace(ImBuf *ibuf) { - struct ImBuf * tbuf1, * tbuf2; + ImBuf * tbuf1, * tbuf2; if (ibuf == NULL) return; if (ibuf->flags & IB_fields) return; @@ -73,9 +73,9 @@ void IMB_de_interlace(struct ImBuf *ibuf) ibuf->y /= 2; } -void IMB_interlace(struct ImBuf *ibuf) +void IMB_interlace(ImBuf *ibuf) { - struct ImBuf * tbuf1, * tbuf2; + ImBuf * tbuf1, * tbuf2; if (ibuf == NULL) return; ibuf->flags &= ~IB_fields; @@ -100,14 +100,76 @@ void IMB_interlace(struct ImBuf *ibuf) } } +/************************* Floyd-Steinberg dithering *************************/ + +typedef struct DitherContext { + int *error_buf, *e; + int v[4], v0[4], v1[4]; + float f; +} DitherContext; + +DitherContext *create_dither_context(int w, float factor) +{ + DitherContext *di; + int i; + + di= MEM_callocN(sizeof(DitherContext), "dithering context"); + di->f= factor / 16.0f; + di->error_buf= MEM_callocN(4*(w+1)*sizeof(int), "dithering error"); + di->e= di->error_buf; + + for(i=0; i<4; ++i) + di->v[i]= di->v0[i]= di->v1[i]= 1024.0f*(BLI_frand()-0.5f); + + return di; +} + +static void clear_dither_context(DitherContext *di) +{ + MEM_freeN(di->error_buf); + MEM_freeN(di); +} + +static void dither_finish_row(DitherContext *di) +{ + int i; + + for(i=0; i<4; i++) + di->v[i]= di->v0[i]= di->v1[i] = 0; + + di->e= di->error_buf; +} + +MINLINE unsigned char dither_value(unsigned short v_in, DitherContext *di, int i) +{ + int dv, d2; + unsigned char v_out; + + di->v[i] = v_in + (2*di->v[i] + di->e[4]) * di->f; + CLAMP(di->v[i], 0, 0xFF00); + v_out = USHORTTOUCHAR(di->v[i]); + di->v[i] -= v_out<<8; + dv = di->v[i]; + d2 = di->v[i]<<1; + di->v[i] += d2; + *(di->e++) = di->v[i] + di->v0[i]; + di->v[i] += d2; + + di->v0[i] = di->v[i] + di->v1[i]; + di->v1[i] = dv; + di->v[i] += d2; + + return v_out; +} + /************************* Generic Buffer Conversion *************************/ MINLINE void byte_to_float_v4(float f[4], const uchar b[4]) { - f[0] = b[0] * (1.0f/255.0f); - f[1] = b[1] * (1.0f/255.0f); - f[2] = b[2] * (1.0f/255.0f); - f[3] = b[3] * (1.0f/255.0f); + f[0]= b[0] * (1.0f/255.0f); + f[1]= b[1] * (1.0f/255.0f); + f[2]= b[2] * (1.0f/255.0f); + f[3]= b[3] * (1.0f/255.0f); } MINLINE void float_to_byte_v4(uchar b[4], const float f[4]) @@ -115,10 +177,26 @@ MINLINE void float_to_byte_v4(uchar b[4], const float f[4]) F4TOCHAR4(f, b); } -MINLINE void float_to_byte_dither_v4(uchar b[4], const float f[4], float dither) +MINLINE void ushort_to_byte_v4(uchar b[4], const unsigned short us[4]) { - float tmp[4] = {f[0]+dither, f[1]+dither, f[2]+dither, f[3]+dither}; - float_to_byte_v4(b, tmp); + b[0]= USHORTTOUCHAR(us[0]); + b[1]= USHORTTOUCHAR(us[1]); + b[2]= USHORTTOUCHAR(us[2]); + b[3]= USHORTTOUCHAR(us[3]); +} + +MINLINE void ushort_to_byte_dither_v4(uchar b[4], const unsigned short us[4], DitherContext *di) +{ + b[0]= dither_value(us[0], di, 0); + b[1]= dither_value(us[0], di, 1); + b[2]= dither_value(us[0], di, 2); + b[3]= dither_value(us[0], di, 3); +} + +MINLINE void float_to_byte_dither_v4(uchar b[4], const float f[4], DitherContext *di) +{ + unsigned short us[4] = {FTOUSHORT(f[0]), FTOUSHORT(f[1]), FTOUSHORT(f[2]), FTOUSHORT(f[3])}; + ushort_to_byte_dither_v4(b, us, di); } /* float to byte pixels, output 4-channel RGBA */ @@ -127,26 +205,28 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from, int width, int height, int stride_to, int stride_from) { float tmp[4]; - float dither_fac = dither/255.0f; int x, y; + DitherContext *di; /* we need valid profiles */ BLI_assert(profile_to != IB_PROFILE_NONE); BLI_assert(profile_from != IB_PROFILE_NONE); - if(channels_from==1) { + BLI_init_srgb_conversion(); + if(dither) + di= create_dither_context(width, dither); + + for(y = 0; y < height; y++) { + if(channels_from == 1) { /* single channel input */ - for(y = 0; y < height; y++) { const float *from = rect_from + stride_from*y; uchar *to = rect_to + stride_to*y*4; for(x = 0; x < width; x++, from++, to+=4) to[0] = to[1] = to[2] = to[3] = FTOCHAR(from[0]); } - } - else if(channels_from == 3) { + else if(channels_from == 3) { /* RGB input */ - for(y = 0; y < height; y++) { const float *from = rect_from + stride_from*y*3; uchar *to = rect_to + stride_to*y*4; @@ -174,10 +254,8 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from, } } } - } - else if(channels_from == 4) { + else if(channels_from == 4) { /* RGBA input */ - for(y = 0; y < height; y++) { const float *from = rect_from + stride_from*y*4; uchar *to = rect_to + stride_to*y*4; @@ -185,7 +263,7 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from, /* no color space conversion */ if(dither) { for(x = 0; x < width; x++, from+=4, to+=4) - float_to_byte_dither_v4(to, from, (BLI_frand()-0.5f)*dither_fac); + float_to_byte_dither_v4(to, from, di); } else { for(x = 0; x < width; x++, from+=4, to+=4) @@ -194,28 +272,30 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from, } else if(profile_to == IB_PROFILE_SRGB) { /* convert from linear to sRGB */ + unsigned short us[4]; + if(dither && predivide) { for(x = 0; x < width; x++, from+=4, to+=4) { - linearrgb_to_srgb_predivide_v4(tmp, from); - float_to_byte_dither_v4(to, tmp, (BLI_frand()-0.5f)*dither_fac); + linearrgb_to_srgb_ushort4_predivide(us, from); + ushort_to_byte_dither_v4(to, us, di); } } else if(dither) { for(x = 0; x < width; x++, from+=4, to+=4) { - linearrgb_to_srgb_v4(tmp, from); - float_to_byte_dither_v4(to, tmp, (BLI_frand()-0.5f)*dither_fac); + linearrgb_to_srgb_ushort4(us, from); + ushort_to_byte_dither_v4(to, us, di); } } else if(predivide) { for(x = 0; x < width; x++, from+=4, to+=4) { - linearrgb_to_srgb_predivide_v4(tmp, from); - float_to_byte_v4(to, tmp); + linearrgb_to_srgb_ushort4_predivide(us, from); + ushort_to_byte_v4(to, us); } } else { for(x = 0; x < width; x++, from+=4, to+=4) { - linearrgb_to_srgb_v4(tmp, from); - float_to_byte_v4(to, tmp); + linearrgb_to_srgb_ushort4(us, from); + ushort_to_byte_v4(to, us); } } } @@ -224,13 +304,13 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from, if(dither && predivide) { for(x = 0; x < width; x++, from+=4, to+=4) { srgb_to_linearrgb_predivide_v4(tmp, from); - float_to_byte_dither_v4(to, tmp, (BLI_frand()-0.5f)*dither_fac); + float_to_byte_dither_v4(to, tmp, di); } } else if(dither) { for(x = 0; x < width; x++, from+=4, to+=4) { srgb_to_linearrgb_v4(tmp, from); - float_to_byte_dither_v4(to, tmp, (BLI_frand()-0.5f)*dither_fac); + float_to_byte_dither_v4(to, tmp, di); } } else if(predivide) { @@ -247,7 +327,13 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from, } } } + + if(dither) + dither_finish_row(di); } + + if(dither) + clear_dither_context(di); } /* byte to float pixels, input and output 4-channel RGBA */ @@ -262,6 +348,8 @@ void IMB_buffer_float_from_byte(float *rect_to, const uchar *rect_from, BLI_assert(profile_to != IB_PROFILE_NONE); BLI_assert(profile_from != IB_PROFILE_NONE); + BLI_init_srgb_conversion(); + /* RGBA input */ for(y = 0; y < height; y++) { const uchar *from = rect_from + stride_from*y*4; @@ -276,14 +364,12 @@ void IMB_buffer_float_from_byte(float *rect_to, const uchar *rect_from, /* convert sRGB to linear */ if(predivide) { for(x = 0; x < width; x++, from+=4, to+=4) { - byte_to_float_v4(tmp, from); - srgb_to_linearrgb_predivide_v4(to, tmp); + srgb_to_linearrgb_uchar4_predivide(to, from); } } else { for(x = 0; x < width; x++, from+=4, to+=4) { - byte_to_float_v4(tmp, from); - srgb_to_linearrgb_v4(to, tmp); + srgb_to_linearrgb_uchar4(to, from); } } } @@ -451,7 +537,7 @@ void IMB_buffer_byte_from_byte(uchar *rect_to, const uchar *rect_from, /****************************** ImBuf Conversion *****************************/ -void IMB_rect_from_float(struct ImBuf *ibuf) +void IMB_rect_from_float(ImBuf *ibuf) { int predivide= (ibuf->flags & IB_cm_predivide); int profile_from; @@ -482,7 +568,7 @@ void IMB_rect_from_float(struct ImBuf *ibuf) } /* converts from linear float to sRGB byte for part of the texture, buffer will hold the changed part */ -void IMB_partial_rect_from_float(struct ImBuf *ibuf, float *buffer, int x, int y, int w, int h) +void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w, int h) { float *rect_float; uchar *rect_byte; @@ -521,7 +607,7 @@ void IMB_partial_rect_from_float(struct ImBuf *ibuf, float *buffer, int x, int y ibuf->userflags &= ~IB_RECT_INVALID; } -void IMB_float_from_rect(struct ImBuf *ibuf) +void IMB_float_from_rect(ImBuf *ibuf) { int predivide= (ibuf->flags & IB_cm_predivide); int profile_from; @@ -547,7 +633,7 @@ void IMB_float_from_rect(struct ImBuf *ibuf) } /* no profile conversion */ -void IMB_float_from_rect_simple(struct ImBuf *ibuf) +void IMB_float_from_rect_simple(ImBuf *ibuf) { int predivide= (ibuf->flags & IB_cm_predivide); @@ -559,7 +645,7 @@ void IMB_float_from_rect_simple(struct ImBuf *ibuf) ibuf->x, ibuf->y, ibuf->x, ibuf->x); } -void IMB_convert_profile(struct ImBuf *ibuf, int profile) +void IMB_convert_profile(ImBuf *ibuf, int profile) { int predivide= (ibuf->flags & IB_cm_predivide); int profile_from, profile_to; @@ -601,7 +687,7 @@ void IMB_convert_profile(struct ImBuf *ibuf, int profile) /* use when you need to get a buffer with a certain profile * if the return */ -float *IMB_float_profile_ensure(struct ImBuf *ibuf, int profile, int *alloc) +float *IMB_float_profile_ensure(ImBuf *ibuf, int profile, int *alloc) { int predivide= (ibuf->flags & IB_cm_predivide); int profile_from, profile_to; @@ -649,7 +735,7 @@ float *IMB_float_profile_ensure(struct ImBuf *ibuf, int profile, int *alloc) /**************************** Color to Grayscale *****************************/ /* no profile conversion */ -void IMB_color_to_bw(struct ImBuf *ibuf) +void IMB_color_to_bw(ImBuf *ibuf) { float *rctf= ibuf->rect_float; uchar *rct= (uchar*)ibuf->rect; |