From db7e2935cb45c061d9aad94f4c96ec34be6057e9 Mon Sep 17 00:00:00 2001 From: Albert Cahalan Date: Mon, 6 Dec 2004 01:09:31 +0000 Subject: [PATCH] Tinter now 2x to 4x faster --- docs/CHANGES.txt | 5 +- src/tuxpaint.c | 823 +++++++++++++++++++++++++++++------------------ 2 files changed, 516 insertions(+), 312 deletions(-) diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt index 22144d5ee..1ccf519f9 100644 --- a/docs/CHANGES.txt +++ b/docs/CHANGES.txt @@ -7,7 +7,10 @@ bill@newbreedsoftware.com http://www.newbreedsoftware.com/tuxpaint/ -2004.November.26 (0.9.15) +2004.December.5 (0.9.15) + * Tinter now 2x to 4x faster. + Albert Cahalan + * Sorry about that leak. Many MB per stamp and nobody complained??? Albert Cahalan diff --git a/src/tuxpaint.c b/src/tuxpaint.c index 699c96c44..4b388e9d1 100644 --- a/src/tuxpaint.c +++ b/src/tuxpaint.c @@ -353,6 +353,42 @@ static void win32_perror(const char * const str) #define clamp(lo,value,hi) (min(max(value,lo),hi)) +// since gcc-2.5 +#ifdef __GNUC__ +#define NORETURN __attribute__((__noreturn__)) +#define FUNCTION __attribute__((__const__)) // no access to global mem, even via ptr, and no side effect +#else +#define NORETURN +#define FUNCTION +#endif + +#if !defined(restrict) && __STDC_VERSION__ < 199901 +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 92 +#define restrict __restrict__ +#else +#warning No restrict keyword? +#define restrict +#endif +#endif + +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 96 +// won't alias anything, and aligned enough for anything +#define MALLOC __attribute__ ((__malloc__)) +// no side effect, may read globals +#define PURE __attribute__ ((__pure__)) +// tell gcc what to expect: if(unlikely(err)) die(err); +#define likely(x) __builtin_expect(!!(x),1) +#define unlikely(x) __builtin_expect(!!(x),0) +#define expected(x,y) __builtin_expect((x),(y)) +#else +#define MALLOC +#define PURE +#define likely(x) (x) +#define unlikely(x) (x) +#define expected(x,y) (x) +#endif + + /* Unfortunately, there is a bug in SDL_ttf-2.0.6, the current version that causes a segmentation fault if an attempt is made to call TTF_OpenFont() with the filename of a font that doesn't exist. This @@ -3028,13 +3064,300 @@ static void blit_brush(int x, int y) #define TINTER_NORMAL 2 // normal #define TINTER_VECTOR 3 // map black->white to black->destination +// This goes from 8-bit sRGB (range 0 to 255) to linear (range 0 to 1). +// The math to produce a table entry: +// tmp = oldvalue / 255.0; +// result = (tmp<=0.03928) ? tmp/12.92 : pow((tmp+0.055)/1.055,2.4); +static const float sRGB_to_linear_table[256] = { + 0.000000, 0.000304, 0.000607, 0.000911, 0.001214, 0.001518, 0.001821, + 0.002125, 0.002428, 0.002732, 0.003035, 0.003347, 0.003677, 0.004025, + 0.004391, 0.004777, 0.005182, 0.005605, 0.006049, 0.006512, 0.006995, + 0.007499, 0.008023, 0.008568, 0.009134, 0.009721, 0.010330, 0.010960, + 0.011612, 0.012286, 0.012983, 0.013702, 0.014444, 0.015209, 0.015996, + 0.016807, 0.017642, 0.018500, 0.019382, 0.020289, 0.021219, 0.022174, + 0.023153, 0.024158, 0.025187, 0.026241, 0.027321, 0.028426, 0.029557, + 0.030713, 0.031896, 0.033105, 0.034340, 0.035601, 0.036889, 0.038204, + 0.039546, 0.040915, 0.042311, 0.043735, 0.045186, 0.046665, 0.048172, + 0.049707, 0.051269, 0.052861, 0.054480, 0.056128, 0.057805, 0.059511, + 0.061246, 0.063010, 0.064803, 0.066626, 0.068478, 0.070360, 0.072272, + 0.074214, 0.076185, 0.078187, 0.080220, 0.082283, 0.084376, 0.086500, + 0.088656, 0.090842, 0.093059, 0.095307, 0.097587, 0.099899, 0.102242, + 0.104616, 0.107023, 0.109462, 0.111932, 0.114435, 0.116971, 0.119538, + 0.122139, 0.124772, 0.127438, 0.130136, 0.132868, 0.135633, 0.138432, + 0.141263, 0.144128, 0.147027, 0.149960, 0.152926, 0.155926, 0.158961, + 0.162029, 0.165132, 0.168269, 0.171441, 0.174647, 0.177888, 0.181164, + 0.184475, 0.187821, 0.191202, 0.194618, 0.198069, 0.201556, 0.205079, + 0.208637, 0.212231, 0.215861, 0.219526, 0.223228, 0.226966, 0.230740, + 0.234551, 0.238398, 0.242281, 0.246201, 0.250158, 0.254152, 0.258183, + 0.262251, 0.266356, 0.270498, 0.274677, 0.278894, 0.283149, 0.287441, + 0.291771, 0.296138, 0.300544, 0.304987, 0.309469, 0.313989, 0.318547, + 0.323143, 0.327778, 0.332452, 0.337164, 0.341914, 0.346704, 0.351533, + 0.356400, 0.361307, 0.366253, 0.371238, 0.376262, 0.381326, 0.386429, + 0.391572, 0.396755, 0.401978, 0.407240, 0.412543, 0.417885, 0.423268, + 0.428690, 0.434154, 0.439657, 0.445201, 0.450786, 0.456411, 0.462077, + 0.467784, 0.473531, 0.479320, 0.485150, 0.491021, 0.496933, 0.502886, + 0.508881, 0.514918, 0.520996, 0.527115, 0.533276, 0.539479, 0.545724, + 0.552011, 0.558340, 0.564712, 0.571125, 0.577580, 0.584078, 0.590619, + 0.597202, 0.603827, 0.610496, 0.617207, 0.623960, 0.630757, 0.637597, + 0.644480, 0.651406, 0.658375, 0.665387, 0.672443, 0.679542, 0.686685, + 0.693872, 0.701102, 0.708376, 0.715694, 0.723055, 0.730461, 0.737910, + 0.745404, 0.752942, 0.760525, 0.768151, 0.775822, 0.783538, 0.791298, + 0.799103, 0.806952, 0.814847, 0.822786, 0.830770, 0.838799, 0.846873, + 0.854993, 0.863157, 0.871367, 0.879622, 0.887923, 0.896269, 0.904661, + 0.913099, 0.921582, 0.930111, 0.938686, 0.947307, 0.955973, 0.964686, + 0.973445, 0.982251, 0.991102, 1.000000, +}; + +// this goes the other way; range checking will be required +static const unsigned char linear_to_sRGB_table[4096] = + "\x00\x01\x02\x03\x03\x04\x05\x06\x07\x08\x08\x09\x0a\x0b\x0b\x0c\x0d\x0d" + "\x0e\x0f\x10\x10\x11\x11\x12\x12\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17" + "\x18\x18\x18\x19\x19\x1a\x1a\x1a\x1b\x1b\x1c\x1c\x1c\x1d\x1d\x1d\x1e\x1e" + "\x1e\x1f\x1f\x1f\x20\x20\x20\x21\x21\x21\x22\x22\x22\x23\x23\x23\x23\x24" + "\x24\x24\x25\x25\x25\x25\x26\x26\x26\x26\x27\x27\x27\x28\x28\x28\x28\x29" + "\x29\x29\x29\x2a\x2a\x2a\x2a\x2b\x2b\x2b\x2b\x2c\x2c\x2c\x2c\x2c\x2d\x2d" + "\x2d\x2d\x2e\x2e\x2e\x2e\x2f\x2f\x2f\x2f\x2f\x30\x30\x30\x30\x30\x31\x31" + "\x31\x31\x31\x32\x32\x32\x32\x33\x33\x33\x33\x33\x34\x34\x34\x34\x34\x35" + "\x35\x35\x35\x35\x35\x36\x36\x36\x36\x36\x37\x37\x37\x37\x37\x38\x38\x38" + "\x38\x38\x38\x39\x39\x39\x39\x39\x39\x3a\x3a\x3a\x3a\x3a\x3a\x3b\x3b\x3b" + "\x3b\x3b\x3c\x3c\x3c\x3c\x3c\x3c\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3e\x3e\x3e" + "\x3e\x3e\x3e\x3f\x3f\x3f\x3f\x3f\x3f\x40\x40\x40\x40\x40\x40\x41\x41\x41" + "\x41\x41\x41\x41\x42\x42\x42\x42\x42\x42\x42\x43\x43\x43\x43\x43\x43\x44" + "\x44\x44\x44\x44\x44\x44\x45\x45\x45\x45\x45\x45\x45\x46\x46\x46\x46\x46" + "\x46\x46\x46\x47\x47\x47\x47\x47\x47\x47\x48\x48\x48\x48\x48\x48\x48\x48" + "\x49\x49\x49\x49\x49\x49\x49\x4a\x4a\x4a\x4a\x4a\x4a\x4a\x4a\x4b\x4b\x4b" + "\x4b\x4b\x4b\x4b\x4c\x4c\x4c\x4c\x4c\x4c\x4c\x4c\x4d\x4d\x4d\x4d\x4d\x4d" + "\x4d\x4d\x4e\x4e\x4e\x4e\x4e\x4e\x4e\x4e\x4f\x4f\x4f\x4f\x4f\x4f\x4f\x4f" + "\x50\x50\x50\x50\x50\x50\x50\x50\x50\x51\x51\x51\x51\x51\x51\x51\x51\x51" + "\x52\x52\x52\x52\x52\x52\x52\x52\x53\x53\x53\x53\x53\x53\x53\x53\x53\x54" + "\x54\x54\x54\x54\x54\x54\x54\x54\x55\x55\x55\x55\x55\x55\x55\x55\x55\x56" + "\x56\x56\x56\x56\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x58" + "\x58\x58\x58\x58\x58\x58\x58\x58\x58\x59\x59\x59\x59\x59\x59\x59\x59\x59" + "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b" + "\x5b\x5b\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5d\x5d\x5d\x5d\x5d\x5d" + "\x5d\x5d\x5d\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5f\x5f\x5f\x5f" + "\x5f\x5f\x5f\x5f\x5f\x5f\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x61" + "\x61\x61\x61\x61\x61\x61\x61\x61\x61\x62\x62\x62\x62\x62\x62\x62\x62\x62" + "\x62\x62\x63\x63\x63\x63\x63\x63\x63\x63\x63\x63\x63\x64\x64\x64\x64\x64" + "\x64\x64\x64\x64\x64\x64\x65\x65\x65\x65\x65\x65\x65\x65\x65\x65\x65\x66" + "\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x67\x67\x67\x67\x67\x67\x67\x67" + "\x67\x67\x67\x67\x68\x68\x68\x68\x68\x68\x68\x68\x68\x68\x68\x69\x69\x69" + "\x69\x69\x69\x69\x69\x69\x69\x69\x6a\x6a\x6a\x6a\x6a\x6a\x6a\x6a\x6a\x6a" + "\x6a\x6a\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6c\x6c\x6c\x6c" + "\x6c\x6c\x6c\x6c\x6c\x6c\x6c\x6c\x6c\x6d\x6d\x6d\x6d\x6d\x6d\x6d\x6d\x6d" + "\x6d\x6d\x6d\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6f\x6f\x6f" + "\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x70\x70\x70\x70\x70\x70\x70\x70\x70" + "\x70\x70\x70\x70\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x72" + "\x72\x72\x72\x72\x72\x72\x72\x72\x72\x72\x72\x72\x73\x73\x73\x73\x73\x73" + "\x73\x73\x73\x73\x73\x73\x73\x74\x74\x74\x74\x74\x74\x74\x74\x74\x74\x74" + "\x74\x74\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x76\x76" + "\x76\x76\x76\x76\x76\x76\x76\x76\x76\x76\x76\x77\x77\x77\x77\x77\x77\x77" + "\x77\x77\x77\x77\x77\x77\x77\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78" + "\x78\x78\x78\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x7a" + "\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7b\x7b\x7b\x7b\x7b\x7b" + "\x7b\x7b\x7b\x7b\x7b\x7b\x7b\x7b\x7b\x7c\x7c\x7c\x7c\x7c\x7c\x7c\x7c\x7c" + "\x7c\x7c\x7c\x7c\x7c\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d" + "\x7d\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7f\x7f" + "\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x80\x80\x80\x80\x80" + "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x81\x81\x81\x81\x81\x81\x81\x81" + "\x81\x81\x81\x81\x81\x81\x81\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82" + "\x82\x82\x82\x82\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83" + "\x83\x83\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x85" + "\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x86\x86\x86" + "\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x87\x87\x87\x87\x87" + "\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x88\x88\x88\x88\x88\x88\x88" + "\x88\x88\x88\x88\x88\x88\x88\x88\x88\x89\x89\x89\x89\x89\x89\x89\x89\x89" + "\x89\x89\x89\x89\x89\x89\x89\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a" + "\x8a\x8a\x8a\x8a\x8a\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b" + "\x8b\x8b\x8b\x8b\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c" + "\x8c\x8c\x8c\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d" + "\x8d\x8d\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e" + "\x8e\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f" + "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x91" + "\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x92" + "\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x93" + "\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x94\x94" + "\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x95\x95" + "\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x96\x96" + "\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x97" + "\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x98" + "\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98" + "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" + "\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a" + "\x9a\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b" + "\x9b\x9b\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c" + "\x9c\x9c\x9c\x9c\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d" + "\x9d\x9d\x9d\x9d\x9d\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e" + "\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f" + "\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0" + "\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1" + "\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa2\xa2\xa2\xa2\xa2\xa2" + "\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa3\xa3\xa3\xa3" + "\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa4" + "\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4" + "\xa4\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5" + "\xa5\xa5\xa5\xa5\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" + "\xa6\xa6\xa6\xa6\xa6\xa6\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7" + "\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8" + "\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa9\xa9\xa9\xa9\xa9" + "\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab" + "\xab\xab\xab\xab\xab\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac" + "\xac\xac\xac\xac\xac\xac\xac\xac\xac\xad\xad\xad\xad\xad\xad\xad\xad\xad" + "\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xae\xae\xae\xae\xae" + "\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xaf" + "\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf" + "\xaf\xaf\xaf\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0" + "\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1" + "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb2\xb2\xb2\xb2\xb2\xb2" + "\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb3" + "\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3" + "\xb3\xb3\xb3\xb3\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4" + "\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5" + "\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb6\xb6\xb6" + "\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6" + "\xb6\xb6\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7" + "\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8" + "\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb9\xb9\xb9\xb9" + "\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9" + "\xb9\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba" + "\xba\xba\xba\xba\xba\xba\xba\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb" + "\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbc\xbc\xbc\xbc" + "\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc" + "\xbc\xbc\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd" + "\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe" + "\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbf\xbf" + "\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf" + "\xbf\xbf\xbf\xbf\xbf\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc1\xc1\xc1\xc1\xc1\xc1" + "\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1" + "\xc1\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2" + "\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3" + "\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc4\xc4" + "\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4" + "\xc4\xc4\xc4\xc4\xc4\xc4\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5" + "\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc6\xc6\xc6\xc6" + "\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6" + "\xc6\xc6\xc6\xc6\xc6\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7" + "\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc8\xc8\xc8\xc8\xc8" + "\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8" + "\xc8\xc8\xc8\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9" + "\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xca\xca\xca\xca\xca\xca" + "\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca" + "\xca\xca\xca\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb" + "\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcc\xcc\xcc\xcc\xcc\xcc" + "\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc" + "\xcc\xcc\xcc\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xce\xce\xce\xce\xce" + "\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce" + "\xce\xce\xce\xce\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf" + "\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xd0\xd0\xd0\xd0" + "\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0" + "\xd0\xd0\xd0\xd0\xd0\xd0\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1" + "\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd2\xd2" + "\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2" + "\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3" + "\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3" + "\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4" + "\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd5\xd5\xd5\xd5\xd5\xd5\xd5" + "\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5" + "\xd5\xd5\xd5\xd5\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6" + "\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd7\xd7\xd7" + "\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7" + "\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8" + "\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8" + "\xd8\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9" + "\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xda\xda\xda\xda\xda" + "\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda" + "\xda\xda\xda\xda\xda\xda\xda\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb" + "\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb" + "\xdb\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc" + "\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde" + "\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde" + "\xde\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf" + "\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xe0\xe0\xe0\xe0" + "\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0" + "\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1" + "\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1" + "\xe1\xe1\xe1\xe1\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2" + "\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe3" + "\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3" + "\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe4\xe4\xe4\xe4\xe4\xe4" + "\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4" + "\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5" + "\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5" + "\xe5\xe5\xe5\xe5\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6" + "\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe7" + "\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7" + "\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe8\xe8\xe8\xe8\xe8" + "\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8" + "\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9" + "\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9" + "\xe9\xe9\xe9\xe9\xe9\xe9\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea" + "\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea" + "\xea\xea\xea\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb" + "\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xec" + "\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec" + "\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xed\xed\xed" + "\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed" + "\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xee\xee\xee\xee\xee\xee" + "\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee" + "\xee\xee\xee\xee\xee\xee\xee\xee\xee\xef\xef\xef\xef\xef\xef\xef\xef\xef" + "\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef" + "\xef\xef\xef\xef\xef\xef\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0" + "\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0" + "\xf0\xf0\xf0\xf0\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1" + "\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1" + "\xf1\xf1\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2" + "\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2" + "\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3" + "\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf4\xf4" + "\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4" + "\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf5\xf5\xf5" + "\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5" + "\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf6\xf6\xf6\xf6" + "\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6" + "\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf7\xf7\xf7\xf7\xf7\xf7" + "\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7" + "\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf8\xf8\xf8\xf8\xf8\xf8\xf8" + "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" + "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf9\xf9\xf9\xf9\xf9\xf9\xf9" + "\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9" + "\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa" + "\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa" + "\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb" + "\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb" + "\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc" + "\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc" + "\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd" + "\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd" + "\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +; + +static unsigned char linear_to_sRGB (float linear) FUNCTION; +static unsigned char linear_to_sRGB (float linear) +{ + unsigned slot; + slot = linear*4096.0 + 0.5; + if(slot>4095) + { + if(linear>0.5) + slot = 4095; + else + slot = 0; + } + return linear_to_sRGB_table[slot]; +} + typedef struct multichan { - double r,g,b; // linear - double L,u,v; // L,a,b would be better -- 2-way formula unknown - double hue,sat; - unsigned char or,og,ob; // old 8-bit sRGB values - unsigned char nr,ng,nb; // new 8-bit sRGB values - unsigned char alpha; // 8-bit alpha value + double L,hue,sat; // L,a,b would be better -- 2-way formula unknown + unsigned char or,og,ob,alpha; // old 8-bit values } multichan; #define X0 ((double)0.9505) @@ -3043,24 +3366,21 @@ typedef struct multichan { #define u0_prime ( (4.0 * X0) / (X0 + 15.0*Y0 + 3.0*Z0) ) #define v0_prime ( (9.0 * Y0) / (X0 + 15.0*Y0 + 3.0*Z0) ) -static void fill_multichan(multichan *mc) + +static void fill_multichan(multichan *mc, double *up, double *vp) { - double tmp,X,Y,Z; + double X,Y,Z,u,v; double u_prime, v_prime; /* temp, part of official formula */ double Y_norm, fract; /* severely temp */ - // from 8-bit sRGB to linear RGB - tmp = mc->or / 255.0; - mc->r = (tmp<=0.03928) ? tmp/12.92 : pow((tmp+0.055)/1.055,2.4); - tmp = mc->og / 255.0; - mc->g = (tmp<=0.03928) ? tmp/12.92 : pow((tmp+0.055)/1.055,2.4); - tmp = mc->ob / 255.0; - mc->b = (tmp<=0.03928) ? tmp/12.92 : pow((tmp+0.055)/1.055,2.4); + double r = sRGB_to_linear_table[mc->or]; + double g = sRGB_to_linear_table[mc->og]; + double b = sRGB_to_linear_table[mc->ob]; // coordinate change, RGB --> XYZ - X = 0.4124*mc->r + 0.3576*mc->g + 0.1805*mc->b; - Y = 0.2126*mc->r + 0.7152*mc->g + 0.0722*mc->b; - Z = 0.0193*mc->r + 0.1192*mc->g + 0.9505*mc->b; + X = 0.4124*r + 0.3576*g + 0.1805*b; + Y = 0.2126*r + 0.7152*g + 0.0722*b; + Z = 0.0193*r + 0.1192*g + 0.9505*b; // XYZ --> Luv Y_norm = Y/Y0; @@ -3068,59 +3388,140 @@ static void fill_multichan(multichan *mc) u_prime = 4.0*X*fract; v_prime = 9.0*Y*fract; mc->L = (Y_norm>0.008856) ? 116.0*pow(Y_norm,1.0/3.0)-16.0 : 903.3*Y_norm; - mc->u = 13.0*mc->L*(u_prime - u0_prime); - mc->v = 13.0*mc->L*(v_prime - v0_prime); + u = 13.0*mc->L*(u_prime - u0_prime); + v = 13.0*mc->L*(v_prime - v0_prime); - mc->sat = sqrt(mc->u*mc->u + mc->v*mc->v); - mc->hue = atan2(mc->u,mc->v); + mc->sat = sqrt(u*u + v*v); + mc->hue = atan2(u,v); + if(up) *up = u; + if(vp) *vp = v; } -static void tint_surface(SDL_Surface * tmp_surf, SDL_Surface * surf_ptr) + +static double tint_part_1(multichan *work, SDL_Surface *in) { - unsigned i; - int xx, yy; - - unsigned width = surf_ptr->w; - unsigned height = surf_ptr->h; - - multichan *work = malloc(sizeof *work * width * height); - - // put pixels into a more tolerable form - SDL_LockSurface(surf_ptr); - for (yy = 0; yy < surf_ptr->h; yy++) - { - for (xx = 0; xx < surf_ptr->w; xx++) - { - multichan *mc = work+yy*width+xx; - SDL_GetRGBA(getpixel(surf_ptr, xx, yy), - surf_ptr->format, - &mc->or, &mc->og, &mc->ob, &mc->alpha); - } - } - SDL_UnlockSurface(surf_ptr); - - i = width * height; - while (i--) - { - multichan *mc = work+i; - fill_multichan(mc); - } - - // initial hue guess - double alpha_total = 0; + int xx,yy; double u_total = 0; double v_total = 0; - i = width * height; - while (i--) - { - multichan *mc = work+i; - alpha_total += mc->alpha; - // more weight to opaque high-saturation pixels - u_total += mc->alpha * mc->u * mc->sat; - v_total += mc->alpha * mc->v * mc->sat; - } - double initial_hue = atan2(u_total,v_total); + SDL_LockSurface(in); + for (yy = 0; yy < in->h; yy++) + { + for (xx = 0; xx < in->w; xx++) + { + multichan *mc = work+yy*in->w+xx; + // put pixels into a more tolerable form + SDL_GetRGBA(getpixel(in, xx, yy), + in->format, + &mc->or, &mc->og, &mc->ob, &mc->alpha); + double u,v; + fill_multichan(mc,&u,&v); + // average out u and v, giving more weight to opaque high-saturation pixels + // (this is to take an initial guess at the primary hue) + u_total += mc->alpha * u * mc->sat; + v_total += mc->alpha * v * mc->sat; + } + } + SDL_UnlockSurface(in); + + return atan2(u_total,v_total); +} + + +static void change_colors(SDL_Surface *out, multichan *work, double hue_range, multichan *key_color_ptr) +{ + double lower_hue_1,upper_hue_1,lower_hue_2,upper_hue_2; + int xx,yy; + + // prepare source and destination color info + // should reset hue_range or not? won't bother for now + multichan key_color = *key_color_ptr; // want to work from a copy, for safety + lower_hue_1 = key_color.hue - hue_range; + upper_hue_1 = key_color.hue + hue_range; + if (lower_hue_1 < -M_PI) + { + lower_hue_2 = lower_hue_1 + 2 * M_PI; + upper_hue_2 = upper_hue_1 + 2 * M_PI; + } + else + { + lower_hue_2 = lower_hue_1 - 2 * M_PI; + upper_hue_2 = upper_hue_1 - 2 * M_PI; + } + + // get the destination color set up + multichan dst; + dst.or = color_hexes[cur_color][0]; + dst.og = color_hexes[cur_color][1]; + dst.ob = color_hexes[cur_color][2]; + fill_multichan(&dst,NULL,NULL); + double satratio = dst.sat / key_color.sat; + double slope = (dst.L-key_color.L)/dst.sat; + + SDL_LockSurface(out); + for (yy = 0; yy < out->h; yy++) + { + for (xx = 0; xx < out->w; xx++) + { + multichan *mc = work+yy*out->w+xx; + + double oldhue = mc->hue; + + // if not in the first range, and not in the second range, skip this one + // (really should alpha-blend as a function of hue angle difference) + if( (oldhueupper_hue_1) && (oldhueupper_hue_2) ) + { + putpixel(out, xx, yy, SDL_MapRGBA(out->format, mc->or, mc->og, mc->ob, mc->alpha)); + continue; + } + + // Modify the pixel + double old_sat = mc->sat; + double newsat = old_sat * satratio; + double L = mc->L; + if(dst.sat>0) + L += newsat * slope; // not greyscale destination + else + L += old_sat*(dst.L-key_color.L)/key_color.sat; + + // convert from L,u,v all the way back to sRGB with 8-bit channels + double X,Y,Z; + double u_prime, v_prime; /* temp, part of official formula */ + unsigned tries = 3; +trysat:; + double u = newsat * sin(dst.hue); + double v = newsat * cos(dst.hue); + + // Luv to XYZ + u_prime = u/(13.0*L)+u0_prime; + v_prime = v/(13.0*L)+v0_prime; + Y = (L>7.99959199307) ? Y0*pow((L+16.0)/116.0,3.0) : Y0*L/903.3; + X = 2.25*Y*u_prime/v_prime; + Z = (3.0*Y - 0.75*Y*u_prime)/v_prime - 5.0*Y; + + // coordinate change: XYZ to RGB + double r = 3.2410*X + -1.5374*Y + -0.4986*Z; + double g = -0.9692*X + 1.8760*Y + 0.0416*Z; + double b = 0.0556*X + -0.2040*Y + 1.0570*Z; + + // If it is out of gamut, try to de-saturate it a few times before truncating. + // (the linear_to_sRGB function will truncate) + if((r<=-0.5 || g<=-0.5 || b<=-0.5 || r>=255.5 || g>=255.5 || b>=255.5) && tries--) + { + newsat *= 0.8; + goto trysat; + } + + putpixel(out, xx, yy, + SDL_MapRGBA(out->format, linear_to_sRGB(r), linear_to_sRGB(g), linear_to_sRGB(b), mc->alpha)); + } + } + SDL_UnlockSurface(out); +} + + +static multichan *find_most_saturated(double initial_hue, multichan *work, unsigned i, double *hue_range_ptr) +{ // find the most saturated pixel near the initial hue guess multichan *key_color_ptr = NULL; double hue_range; @@ -3153,7 +3554,6 @@ hue_range_retry:; lower_hue_2 = lower_hue_1 - 2 * M_PI; upper_hue_2 = upper_hue_1 - 2 * M_PI; } - i = width * height; while (i--) { multichan *mc = work+i; @@ -3170,268 +3570,69 @@ hue_range_retry:; hue_range *= 1.5; if (hue_range < M_PI) goto hue_range_retry; - goto give_up; } - - // wider for processing than for searching - hue_range *= 1.5; - - // prepare source and destination color info - // should reset hue_range or not? won't bother for now - multichan key_color = *key_color_ptr; // want to work from a copy, for safety - lower_hue_1 = key_color.hue - hue_range; - upper_hue_1 = key_color.hue + hue_range; - if (lower_hue_1 < -M_PI) - { - lower_hue_2 = lower_hue_1 + 2 * M_PI; - upper_hue_2 = upper_hue_1 + 2 * M_PI; - } - else - { - lower_hue_2 = lower_hue_1 - 2 * M_PI; - upper_hue_2 = upper_hue_1 - 2 * M_PI; - } - - // get the destination color set up - multichan dst; - dst.or = color_hexes[cur_color][0]; - dst.og = color_hexes[cur_color][1]; - dst.ob = color_hexes[cur_color][2]; - fill_multichan(&dst); - double satratio = dst.sat / key_color.sat; - double slope = (dst.L-key_color.L)/dst.sat; - - // change the colors! - i = width * height; - while (i--) - { - multichan *mc = work+i; - // if not in the first range, and not in the second range, skip this one - // (really should alpha-blend as a function of hue angle difference) - if( (mc->huehue>upper_hue_1) && (mc->huehue>upper_hue_2) ) - continue; - // this one will now be modified - double old_sat = mc->sat; - mc->hue = dst.hue; - mc->sat = mc->sat * satratio; - if(dst.sat>0) - mc->L += mc->sat * slope; // not greyscale destination - else - mc->L += old_sat*(dst.L-key_color.L)/key_color.sat; - } - -give_up: - - i = width * height; - while (i--) - { - multichan *mc = work+i; - double X,Y,Z; - double u_prime, v_prime; /* temp, part of official formula */ - int r,g,b; - unsigned tries = 3; - double sat = mc->sat; -trysat:; - double u = sat * sin(mc->hue); - double v = sat * cos(mc->hue); - double L = mc->L; - - // Luv to XYZ - u_prime = u/(13.0*L)+u0_prime; - v_prime = v/(13.0*L)+v0_prime; - Y = (L>7.99959199307) ? Y0*pow((L+16.0)/116.0,3.0) : Y0*L/903.3; - X = 2.25*Y*u_prime/v_prime; - Z = (3.0*Y - 0.75*Y*u_prime)/v_prime - 5.0*Y; - - // coordinate change: XYZ to RGB - mc->r = 3.2410*X + -1.5374*Y + -0.4986*Z; - mc->g = -0.9692*X + 1.8760*Y + 0.0416*Z; - mc->b = 0.0556*X + -0.2040*Y + 1.0570*Z; - - // gamma: linear to sRGB - r = ( (mc->r<=0.00304) ? 12.92*mc->r : 1.055*pow(mc->r,1.0/2.4)-0.055 ) * 255.9999; - g = ( (mc->g<=0.00304) ? 12.92*mc->g : 1.055*pow(mc->g,1.0/2.4)-0.055 ) * 255.9999; - b = ( (mc->b<=0.00304) ? 12.92*mc->b : 1.055*pow(mc->b,1.0/2.4)-0.055 ) * 255.9999; - - if((r|g|b)>>8){ - static int cnt = 42; - if(cnt){ - cnt--; -// printf("%d %d %d\n",r,g,b); - } - sat *= 0.8; - if(tries--) - goto trysat; // maybe it'll work if we de-saturate a bit - else - { // bummer, this is out of gamut and fighting - if (r>255) - r = 255; - if (g>255) - g = 255; - if (b>255) - b = 255; - if (r<0) - r = 0; - if (g<0) - g = 0; - if (b<0) - b = 0; - } - } - - mc->nr = r; - mc->ng = g; - mc->nb = b; - } - - // put data back into SDL form - SDL_LockSurface(tmp_surf); - for (yy = 0; yy < tmp_surf->h; yy++) - { - for (xx = 0; xx < tmp_surf->w; xx++) - { - multichan *mc = work+yy*width+xx; - putpixel(tmp_surf, xx, yy, - SDL_MapRGBA(tmp_surf->format, mc->nr, mc->ng, mc->nb, mc->alpha)); - } - } - SDL_UnlockSurface(tmp_surf); - free(work); + *hue_range_ptr = hue_range; + return key_color_ptr; } -// This tints a greyscale stamp. Hopefully such stamps remain rare. -// If they were common, a more efficient way to do this is to simply -// scale the linear RGB values of the destination color by the linear -// brightness of the stamp colors. -static void vector_tint_surface(SDL_Surface * tmp_surf, SDL_Surface * surf_ptr) -{ - unsigned i; - int xx, yy; +static void vector_tint_surface(SDL_Surface * out, SDL_Surface * in) +{ + int xx,yy; + + double r = sRGB_to_linear_table[color_hexes[cur_color][0]]; + double g = sRGB_to_linear_table[color_hexes[cur_color][1]]; + double b = sRGB_to_linear_table[color_hexes[cur_color][2]]; + + SDL_LockSurface(in); + for (yy = 0; yy < in->h; yy++) + { + for (xx = 0; xx < in->w; xx++) + { + unsigned char r8, g8, b8, a8; + SDL_GetRGBA(getpixel(in, xx, yy), + in->format, + &r8, &g8, &b8, &a8); + // get the linear greyscale value + double old = sRGB_to_linear_table[r8]*0.2126 + sRGB_to_linear_table[g8]*0.7152 + sRGB_to_linear_table[b8]*0.0722; + + putpixel(out, xx, yy, + SDL_MapRGBA(out->format, linear_to_sRGB(r*old), linear_to_sRGB(g*old), linear_to_sRGB(b*old), a8)); + } + } + SDL_UnlockSurface(in); +} + + +static void tint_surface(SDL_Surface * tmp_surf, SDL_Surface * surf_ptr) +{ unsigned width = surf_ptr->w; unsigned height = surf_ptr->h; multichan *work = malloc(sizeof *work * width * height); - // put pixels into a more tolerable form - SDL_LockSurface(surf_ptr); - for (yy = 0; yy < surf_ptr->h; yy++) - { - for (xx = 0; xx < surf_ptr->w; xx++) - { - multichan *mc = work+yy*width+xx; - SDL_GetRGBA(getpixel(surf_ptr, xx, yy), - surf_ptr->format, - &mc->or, &mc->og, &mc->ob, &mc->alpha); - } - } - SDL_UnlockSurface(surf_ptr); + double initial_hue = tint_part_1(work, surf_ptr); - i = width * height; - while (i--) + double hue_range; + multichan *key_color_ptr = find_most_saturated(initial_hue, work, width*height, &hue_range); + + if (key_color_ptr) { - multichan *mc = work+i; - fill_multichan(mc); + // wider for processing than for searching + hue_range *= 1.5; + + change_colors(tmp_surf, work, hue_range, key_color_ptr); + } + else + { + fprintf(stderr, "fallback to tinter=vector, this should be in the *.dat file\n"); + vector_tint_surface(tmp_surf, surf_ptr); } - // get the destination color set up - multichan dst; - dst.or = color_hexes[cur_color][0]; - dst.og = color_hexes[cur_color][1]; - dst.ob = color_hexes[cur_color][2]; - fill_multichan(&dst); - - if (dst.L > 0.0) // if not black (else this is a SLOW memcpy!) - { - // change the colors! - i = width * height; - while (i--) - { - multichan *mc = work+i; - // this one will now be modified - mc->hue = dst.hue; - mc->sat = dst.sat * mc->L / 100.0; - mc->L = mc->L * mc->L / 100.0; - } - } - - i = width * height; - while (i--) - { - multichan *mc = work+i; - double X,Y,Z; - double u_prime, v_prime; /* temp, part of official formula */ - int r,g,b; - unsigned tries = 3; - double sat = mc->sat; -trysat:; - double u = sat * sin(mc->hue); - double v = sat * cos(mc->hue); - double L = mc->L; - - // Luv to XYZ - u_prime = u/(13.0*L)+u0_prime; - v_prime = v/(13.0*L)+v0_prime; - Y = (L>7.99959199307) ? Y0*pow((L+16.0)/116.0,3.0) : Y0*L/903.3; - X = 2.25*Y*u_prime/v_prime; - Z = (3.0*Y - 0.75*Y*u_prime)/v_prime - 5.0*Y; - - // coordinate change: XYZ to RGB - mc->r = 3.2410*X + -1.5374*Y + -0.4986*Z; - mc->g = -0.9692*X + 1.8760*Y + 0.0416*Z; - mc->b = 0.0556*X + -0.2040*Y + 1.0570*Z; - - // gamma: linear to sRGB - r = ( (mc->r<=0.00304) ? 12.92*mc->r : 1.055*pow(mc->r,1.0/2.4)-0.055 ) * 255.9999; - g = ( (mc->g<=0.00304) ? 12.92*mc->g : 1.055*pow(mc->g,1.0/2.4)-0.055 ) * 255.9999; - b = ( (mc->b<=0.00304) ? 12.92*mc->b : 1.055*pow(mc->b,1.0/2.4)-0.055 ) * 255.9999; - - if((r|g|b)>>8){ - static int cnt = 42; - if(cnt){ - cnt--; -// printf("%d %d %d\n",r,g,b); - } - sat *= 0.8; - if(tries--) - goto trysat; // maybe it'll work if we de-saturate a bit - else - { // bummer, this is out of gamut and fighting - if (r>255) - r = 255; - if (g>255) - g = 255; - if (b>255) - b = 255; - if (r<0) - r = 0; - if (g<0) - g = 0; - if (b<0) - b = 0; - } - } - - mc->nr = r; - mc->ng = g; - mc->nb = b; - } - - // put data back into SDL form - SDL_LockSurface(tmp_surf); - for (yy = 0; yy < tmp_surf->h; yy++) - { - for (xx = 0; xx < tmp_surf->w; xx++) - { - multichan *mc = work+yy*width+xx; - putpixel(tmp_surf, xx, yy, - SDL_MapRGBA(tmp_surf->format, mc->nr, mc->ng, mc->nb, mc->alpha)); - } - } - SDL_UnlockSurface(tmp_surf); free(work); } + ////////////////////////////////////////////////////////////////////// /* Draw using the current stamp: */