To show off the new tinter, the enclosed patch restores
the original purple. Yellow is much, much, nicer with
this new tinter.

The one trouble is that pickup truck. It isn't anywhere
near constant hue. It varies from blue to green so much
that going plus-or-minus 25 degrees from the primary hue
is not enough. I could widen that up, but then I would
not be able to have tail lights and turn signals on a
green car remain unaffected by the tinting.

//////////////////////////////

diff -Naurd t2/src/colors.h tx/src/colors.h
--- t2/src/colors.h     2004-11-22 20:28:45.000000000 -0500
+++ tx/src/colors.h     2004-11-25 00:18:52.000000000 -0500
@@ -52,7 +52,7 @@
   { 33, 148,  33}, /* Green */
   {138, 168, 205}, /* Sky blue */
   {  0,   0, 255}, /* Blue */
-  { 96,   0, 128}, /* Purple */
+  {128,   0, 128}, /* Purple */
   {255,   0, 255}, /* Magenta */
   {128,  96,   0}, /* Brown */
   {226, 189, 166}, /* Tan */
diff -Naurd t2/src/tuxpaint.c tx/src/tuxpaint.c
--- t2/src/tuxpaint.c   2004-11-24 18:35:15.000000000 -0500
+++ tx/src/tuxpaint.c   2004-11-25 00:31:35.000000000 -0500
@@ -3009,58 +3009,266 @@
 }
 
 
+//////////////////////////////////////////////////////////////////////////
+// stamp tinter
+
 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,oa; // old 8-bit sRGB values
-  unsigned char nr,ng,nb,na; // new 8-bit sRGB values
+  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
 } multichan;
 
+#define X0 ((double)0.9505)
+#define Y0 ((double)1.0000)
+#define Z0 ((double)1.0890)
+#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)
+{
+  double tmp,X,Y,Z;
+  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);
+
+  // 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;
+  
+  // XYZ --> Luv
+  Y_norm = Y/Y0;
+  fract = 1.0 / (X + 15.0*Y + 3.0*Z);
+  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);
+
+  mc->sat = sqrt(mc->u*mc->u + mc->v*mc->v);
+  mc->hue = atan2(mc->u,mc->v);
+}
+
 static void tint_surface(SDL_Surface * tmp_surf, SDL_Surface * surf_ptr)
 {
+  unsigned i;
+  int xx, yy;
 
-  float col_hue, col_sat, col_val,
-    stamp_hue, stamp_sat, stamp_val;
+  unsigned width = surf_ptr->w;
+  unsigned height = surf_ptr->h;
 
-  Uint8 r, g, b, a;
-    int xx, yy;
+  multichan *work = malloc(sizeof *work * width * height);
          
-      rgbtohsv(color_hexes[cur_color][0],
-              color_hexes[cur_color][1],
-              color_hexes[cur_color][2],
-              &col_hue, &col_sat, &col_val);
-        
+  // 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);
 
-      /* Render the stamp: */
+  i = width * height;
+  while (i--)
+    {
+      multichan *mc = work+i;
+      fill_multichan(mc);
+    }
 
-      SDL_LockSurface(surf_ptr);
-      SDL_LockSurface(tmp_surf);
+  // initial hue guess
+  double alpha_total = 0;
+  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);
 
-      for (yy = 0; yy < surf_ptr->h; yy++)
-       {
-         for (xx = 0; xx < surf_ptr->w; xx++)
-           {
-             SDL_GetRGBA(getpixel(surf_ptr, xx, yy),
-                         surf_ptr->format,
-                         &r, &g, &b, &a);
-       
-             rgbtohsv(r, g, b, &stamp_hue, &stamp_sat, &stamp_val);    
-     
-             if ( stamp_tintgray(cur_stamp) || stamp_sat > 0.25 )
-               hsvtorgb(col_hue, col_sat, stamp_val, &r, &g, &b);
-             //        else
-             //          hsvtorgb(col_hue, col_sat, stamp_val, &r, &g, &b);
-       
-             putpixel(tmp_surf, xx, yy,
-                      SDL_MapRGBA(tmp_surf->format, r, g, b, a));
-           }
+  // find the most saturated pixel near the initial hue guess
+  multichan *key_color_ptr = NULL;
+  double hue_range = 25*M_PI/180.0; // plus or minus 25 degrees (should let 
stamp author decide)
+hue_range_retry:;
+  double max_sat = 0;
+  double lower_hue_1 = initial_hue - hue_range;
+  double upper_hue_1 = initial_hue + hue_range;
+  double lower_hue_2;
+  double upper_hue_2;
+  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;
+    }
+  i = width * height;
+  while (i--)
+    {
+      multichan *mc = work+i;
+      // if not in the first range, and not in the second range, skip this one
+      if( (mc->hue<lower_hue_1 || mc->hue>upper_hue_1) && (mc->hue<lower_hue_2 
|| mc->hue>upper_hue_2) )
+       continue;
+      if(mc->sat > max_sat) {
+       max_sat = mc->sat;
+       key_color_ptr = mc;
+      }
+    }
+  if (!key_color_ptr)
+    {
+      hue_range *= 1.5;
+      if (hue_range < M_PI)
+       goto hue_range_retry;
+      goto give_up;
+    }
+
+
+  // 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->hue<lower_hue_1 || mc->hue>upper_hue_1) && (mc->hue<lower_hue_2 
|| mc->hue>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;
+    }
 
-      SDL_UnlockSurface(tmp_surf);
-      SDL_UnlockSurface(surf_ptr);
+  // 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);
 }
 
+//////////////////////////////////////////////////////////////////////
+
 /* Draw using the current stamp: */
 
 static void stamp_draw(int x, int y)


_______________________________________________
Tuxpaint-dev mailing list
[EMAIL PROTECTED]
http://tux4kids.net/mailman/listinfo/tuxpaint-dev

Reply via email to