gradient.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity gradient rendering */
00004 
00005 /* 
00006  * Copyright (C) 2001 Havoc Pennington, 99% copied from wrlib in
00007  * WindowMaker, Copyright (C) 1997-2000 Dan Pascu and Alfredo Kojima
00008  * Copyright (C) 2005 Elijah Newren
00009  * 
00010  * This program is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU General Public License as
00012  * published by the Free Software Foundation; either version 2 of the
00013  * License, or (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * General Public License for more details.
00019  * 
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00023  * 02111-1307, USA.  */
00024 
00025 #include "gradient.h"
00026 #include "util.h"
00027 #include <string.h>
00028 
00029 /* This is all Alfredo's and Dan's usual very nice WindowMaker code,
00030  * slightly GTK-ized
00031  */
00032 static GdkPixbuf* meta_gradient_create_horizontal       (int             width,
00033                                                          int             height,
00034                                                          const GdkColor *from,
00035                                                          const GdkColor *to);
00036 static GdkPixbuf* meta_gradient_create_vertical         (int             width,
00037                                                          int             height,
00038                                                          const GdkColor *from,
00039                                                          const GdkColor *to);
00040 static GdkPixbuf* meta_gradient_create_diagonal         (int             width,
00041                                                          int             height,
00042                                                          const GdkColor *from,
00043                                                          const GdkColor *to);
00044 static GdkPixbuf* meta_gradient_create_multi_horizontal (int             width,
00045                                                          int             height,
00046                                                          const GdkColor *colors,
00047                                                          int             count);
00048 static GdkPixbuf* meta_gradient_create_multi_vertical   (int             width,
00049                                                          int             height,
00050                                                          const GdkColor *colors,
00051                                                          int             count);
00052 static GdkPixbuf* meta_gradient_create_multi_diagonal   (int             width,
00053                                                          int             height,
00054                                                          const GdkColor *colors,
00055                                                          int             count);
00056 
00057 
00058 /* Used as the destroy notification function for gdk_pixbuf_new() */
00059 static void
00060 free_buffer (guchar *pixels, gpointer data)
00061 {
00062   g_free (pixels);
00063 }
00064 
00065 static GdkPixbuf*
00066 blank_pixbuf (int width, int height, gboolean no_padding)
00067 {
00068   guchar *buf;
00069   int rowstride;
00070 
00071   g_return_val_if_fail (width > 0, NULL);
00072   g_return_val_if_fail (height > 0, NULL);
00073 
00074   if (no_padding)
00075     rowstride = width * 3;
00076   else
00077     /* Always align rows to 32-bit boundaries */  
00078     rowstride = 4 * ((3 * width + 3) / 4);
00079 
00080   buf = g_try_malloc (height * rowstride);
00081   if (!buf)
00082     return NULL;
00083 
00084   return gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB,
00085                                    FALSE, 8,
00086                                    width, height, rowstride,
00087                                    free_buffer, NULL);
00088 }
00089 
00090 GdkPixbuf*
00091 meta_gradient_create_simple (int              width,
00092                              int              height,
00093                              const GdkColor  *from,
00094                              const GdkColor  *to,
00095                              MetaGradientType style)
00096 {
00097   switch (style)
00098     {
00099     case META_GRADIENT_HORIZONTAL:
00100       return meta_gradient_create_horizontal (width, height,
00101                                               from, to);
00102     case META_GRADIENT_VERTICAL:
00103       return meta_gradient_create_vertical (width, height,
00104                                             from, to);
00105 
00106     case META_GRADIENT_DIAGONAL:
00107       return meta_gradient_create_diagonal (width, height,
00108                                             from, to);
00109     case META_GRADIENT_LAST:
00110       break;
00111     }
00112   g_assert_not_reached ();
00113   return NULL;
00114 }
00115 
00116 GdkPixbuf*
00117 meta_gradient_create_multi (int              width,
00118                             int              height,
00119                             const GdkColor  *colors,
00120                             int              n_colors,
00121                             MetaGradientType style)
00122 {
00123 
00124   if (n_colors > 2)
00125     {
00126       switch (style)
00127         {
00128         case META_GRADIENT_HORIZONTAL:
00129           return meta_gradient_create_multi_horizontal (width, height, colors, n_colors);
00130         case META_GRADIENT_VERTICAL:
00131           return meta_gradient_create_multi_vertical (width, height, colors, n_colors);
00132         case META_GRADIENT_DIAGONAL:
00133           return meta_gradient_create_multi_diagonal (width, height, colors, n_colors);
00134         case META_GRADIENT_LAST:
00135           g_assert_not_reached ();
00136           break;
00137         }
00138     }
00139   else if (n_colors > 1)
00140     {
00141       return meta_gradient_create_simple (width, height, &colors[0], &colors[1],
00142                                           style);
00143     }
00144   else if (n_colors > 0)
00145     {
00146       return meta_gradient_create_simple (width, height, &colors[0], &colors[0],
00147                                           style);
00148     }
00149   g_assert_not_reached ();
00150   return NULL;
00151 }
00152 
00153 /* Interwoven essentially means we have two vertical gradients,
00154  * cut into horizontal strips of the given thickness, and then the strips
00155  * are alternated. I'm not sure what it's good for, just copied since
00156  * WindowMaker had it.
00157  */
00158 GdkPixbuf*
00159 meta_gradient_create_interwoven (int            width,
00160                                  int            height,
00161                                  const GdkColor colors1[2],
00162                                  int            thickness1,
00163                                  const GdkColor colors2[2],
00164                                  int            thickness2)
00165 {
00166   
00167   int i, j, k, l, ll;
00168   long r1, g1, b1, dr1, dg1, db1;
00169   long r2, g2, b2, dr2, dg2, db2;
00170   GdkPixbuf *pixbuf;
00171   unsigned char *ptr;
00172   unsigned char *pixels;
00173   int rowstride;
00174   
00175   pixbuf = blank_pixbuf (width, height, FALSE);
00176   if (pixbuf == NULL)
00177     return NULL;
00178     
00179   pixels = gdk_pixbuf_get_pixels (pixbuf);
00180   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00181   
00182   r1 = colors1[0].red<<8;
00183   g1 = colors1[0].green<<8;
00184   b1 = colors1[0].blue<<8;
00185 
00186   r2 = colors2[0].red<<8;
00187   g2 = colors2[0].green<<8;
00188   b2 = colors2[0].blue<<8;
00189 
00190   dr1 = ((colors1[1].red-colors1[0].red)<<8)/(int)height;
00191   dg1 = ((colors1[1].green-colors1[0].green)<<8)/(int)height;
00192   db1 = ((colors1[1].blue-colors1[0].blue)<<8)/(int)height;
00193 
00194   dr2 = ((colors2[1].red-colors2[0].red)<<8)/(int)height;
00195   dg2 = ((colors2[1].green-colors2[0].green)<<8)/(int)height;
00196   db2 = ((colors2[1].blue-colors2[0].blue)<<8)/(int)height;
00197 
00198   for (i=0,k=0,l=0,ll=thickness1; i<height; i++)
00199     {
00200       ptr = pixels + i * rowstride;
00201       
00202       if (k == 0)
00203         {
00204           ptr[0] = (unsigned char) (r1>>16);
00205           ptr[1] = (unsigned char) (g1>>16);
00206           ptr[2] = (unsigned char) (b1>>16);
00207         }
00208       else
00209         {
00210           ptr[0] = (unsigned char) (r2>>16);
00211           ptr[1] = (unsigned char) (g2>>16);
00212           ptr[2] = (unsigned char) (b2>>16);
00213         }
00214 
00215       for (j=1; j <= width/2; j *= 2)
00216         memcpy (&(ptr[j*3]), ptr, j*3);
00217       memcpy (&(ptr[j*3]), ptr, (width - j)*3);
00218 
00219       if (++l == ll)
00220         {
00221           if (k == 0)
00222             {
00223               k = 1;
00224               ll = thickness2;
00225             }
00226           else
00227             {
00228               k = 0;
00229               ll = thickness1;
00230             }
00231           l = 0;
00232         }
00233       r1+=dr1;
00234       g1+=dg1;
00235       b1+=db1;
00236         
00237       r2+=dr2;
00238       g2+=dg2;
00239       b2+=db2;
00240     }
00241 
00242   return pixbuf;
00243 }
00244 
00245 /*
00246  *----------------------------------------------------------------------
00247  * meta_gradient_create_horizontal--
00248  *      Renders a horizontal linear gradient of the specified size in the
00249  * GdkPixbuf format with a border of the specified type. 
00250  * 
00251  * Returns:
00252  *      A 24bit GdkPixbuf with the gradient (no alpha channel).
00253  * 
00254  * Side effects:
00255  *      None
00256  *---------------------------------------------------------------------- 
00257  */
00258 static GdkPixbuf*
00259 meta_gradient_create_horizontal (int width, int height,
00260                                  const GdkColor *from,
00261                                  const GdkColor *to)
00262 {    
00263   int i;
00264   long r, g, b, dr, dg, db;
00265   GdkPixbuf *pixbuf;
00266   unsigned char *ptr;
00267   unsigned char *pixels;
00268   int r0, g0, b0;
00269   int rf, gf, bf;
00270   int rowstride;
00271 
00272   pixbuf = blank_pixbuf (width, height, FALSE);
00273   if (pixbuf == NULL)
00274     return NULL;
00275     
00276   pixels = gdk_pixbuf_get_pixels (pixbuf);
00277   ptr = pixels;
00278   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00279   
00280   r0 = (guchar) (from->red / 256.0);
00281   g0 = (guchar) (from->green / 256.0);
00282   b0 = (guchar) (from->blue / 256.0);
00283   rf = (guchar) (to->red / 256.0);
00284   gf = (guchar) (to->green / 256.0);
00285   bf = (guchar) (to->blue / 256.0);  
00286   
00287   r = r0 << 16;
00288   g = g0 << 16;
00289   b = b0 << 16;
00290     
00291   dr = ((rf-r0)<<16)/(int)width;
00292   dg = ((gf-g0)<<16)/(int)width;
00293   db = ((bf-b0)<<16)/(int)width;
00294   /* render the first line */
00295   for (i=0; i<width; i++)
00296     {
00297       *(ptr++) = (unsigned char)(r>>16);
00298       *(ptr++) = (unsigned char)(g>>16);
00299       *(ptr++) = (unsigned char)(b>>16);
00300       r += dr;
00301       g += dg;
00302       b += db;
00303     }
00304 
00305   /* copy the first line to the other lines */
00306   for (i=1; i<height; i++)
00307     {
00308       memcpy (&(pixels[i*rowstride]), pixels, rowstride);
00309     }
00310   return pixbuf;
00311 }
00312 
00313 /*
00314  *----------------------------------------------------------------------
00315  * meta_gradient_create_vertical--
00316  *      Renders a vertical linear gradient of the specified size in the
00317  * GdkPixbuf format with a border of the specified type.
00318  *
00319  * Returns:
00320  *      A 24bit GdkPixbuf with the gradient (no alpha channel).
00321  *
00322  * Side effects:
00323  *      None
00324  *----------------------------------------------------------------------
00325  */
00326 static GdkPixbuf*
00327 meta_gradient_create_vertical (int width, int height,
00328                                const GdkColor *from,
00329                                const GdkColor *to)
00330 {
00331   int i, j;
00332   long r, g, b, dr, dg, db;
00333   GdkPixbuf *pixbuf;
00334   unsigned char *ptr;
00335   int r0, g0, b0;
00336   int rf, gf, bf;
00337   int rowstride;
00338   unsigned char *pixels;
00339   
00340   pixbuf = blank_pixbuf (width, height, FALSE);
00341   if (pixbuf == NULL)
00342     return NULL;
00343     
00344   pixels = gdk_pixbuf_get_pixels (pixbuf);
00345   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00346   
00347   r0 = (guchar) (from->red / 256.0);
00348   g0 = (guchar) (from->green / 256.0);
00349   b0 = (guchar) (from->blue / 256.0);
00350   rf = (guchar) (to->red / 256.0);
00351   gf = (guchar) (to->green / 256.0);
00352   bf = (guchar) (to->blue / 256.0);
00353   
00354   r = r0<<16;
00355   g = g0<<16;
00356   b = b0<<16;
00357 
00358   dr = ((rf-r0)<<16)/(int)height;
00359   dg = ((gf-g0)<<16)/(int)height;
00360   db = ((bf-b0)<<16)/(int)height;
00361 
00362   for (i=0; i<height; i++)
00363     {
00364       ptr = pixels + i * rowstride;
00365       
00366       ptr[0] = (unsigned char)(r>>16);
00367       ptr[1] = (unsigned char)(g>>16);
00368       ptr[2] = (unsigned char)(b>>16);
00369 
00370       for (j=1; j <= width/2; j *= 2)
00371         memcpy (&(ptr[j*3]), ptr, j*3);
00372       memcpy (&(ptr[j*3]), ptr, (width - j)*3);
00373 
00374       r+=dr;
00375       g+=dg;
00376       b+=db;
00377     }
00378   return pixbuf;
00379 }
00380 
00381 
00382 /*
00383  *----------------------------------------------------------------------
00384  * meta_gradient_create_diagonal--
00385  *      Renders a diagonal linear gradient of the specified size in the
00386  * GdkPixbuf format with a border of the specified type.
00387  *
00388  * Returns:
00389  *      A 24bit GdkPixbuf with the gradient (no alpha channel).
00390  *
00391  * Side effects:
00392  *      None
00393  *----------------------------------------------------------------------
00394  */
00395 
00396 
00397 static GdkPixbuf*
00398 meta_gradient_create_diagonal (int width, int height,
00399                                const GdkColor *from,
00400                                const GdkColor *to)
00401 {
00402   GdkPixbuf *pixbuf, *tmp;
00403   int j;
00404   float a, offset;
00405   unsigned char *ptr;
00406   unsigned char *pixels;
00407   int rowstride;
00408   
00409   if (width == 1)
00410     return meta_gradient_create_vertical (width, height, from, to);
00411   else if (height == 1)
00412     return meta_gradient_create_horizontal (width, height, from, to);
00413 
00414   pixbuf = blank_pixbuf (width, height, FALSE);
00415   if (pixbuf == NULL)
00416     return NULL;
00417     
00418   pixels = gdk_pixbuf_get_pixels (pixbuf);
00419   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00420 
00421   tmp = meta_gradient_create_horizontal (2*width-1, 1, from, to);
00422   if (!tmp)
00423     {
00424       g_object_unref (G_OBJECT (pixbuf));
00425       return NULL;
00426     }
00427 
00428   ptr = gdk_pixbuf_get_pixels (tmp);
00429 
00430   a = ((float)(width - 1))/((float)(height - 1));
00431   width = width * 3;
00432 
00433   /* copy the first line to the other lines with corresponding offset */
00434   for (j=0, offset=0.0; j<rowstride*height; j += rowstride)
00435     {
00436       memcpy (&(pixels[j]), &ptr[3*(int)offset], width);
00437       offset += a;
00438     }
00439 
00440   g_object_unref (G_OBJECT (tmp));
00441   return pixbuf;
00442 }
00443 
00444 
00445 static GdkPixbuf*
00446 meta_gradient_create_multi_horizontal (int width, int height,
00447                                        const GdkColor *colors,
00448                                        int count)
00449 {
00450   int i, j, k;
00451   long r, g, b, dr, dg, db;
00452   GdkPixbuf *pixbuf;
00453   unsigned char *ptr;
00454   unsigned char *pixels;
00455   int width2;  
00456   int rowstride;
00457   
00458   g_return_val_if_fail (count > 2, NULL);
00459 
00460   pixbuf = blank_pixbuf (width, height, FALSE);
00461   if (pixbuf == NULL)
00462     return NULL;
00463     
00464   pixels = gdk_pixbuf_get_pixels (pixbuf);
00465   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00466   ptr = pixels;
00467     
00468   if (count > width)
00469     count = width;
00470     
00471   if (count > 1)
00472     width2 = width/(count-1);
00473   else
00474     width2 = width;
00475     
00476   k = 0;
00477 
00478   r = colors[0].red << 8;
00479   g = colors[0].green << 8;
00480   b = colors[0].blue << 8;
00481 
00482   /* render the first line */
00483   for (i=1; i<count; i++)
00484     {
00485       dr = ((int)(colors[i].red   - colors[i-1].red)  <<8)/(int)width2;
00486       dg = ((int)(colors[i].green - colors[i-1].green)<<8)/(int)width2;
00487       db = ((int)(colors[i].blue  - colors[i-1].blue) <<8)/(int)width2;
00488       for (j=0; j<width2; j++)
00489         {
00490           *ptr++ = (unsigned char)(r>>16);
00491           *ptr++ = (unsigned char)(g>>16);
00492           *ptr++ = (unsigned char)(b>>16);
00493           r += dr;
00494           g += dg;
00495           b += db;
00496           k++;
00497         }
00498       r = colors[i].red << 8;
00499       g = colors[i].green << 8;
00500       b = colors[i].blue << 8;
00501     }
00502   for (j=k; j<width; j++)
00503     {
00504       *ptr++ = (unsigned char)(r>>16);
00505       *ptr++ = (unsigned char)(g>>16);
00506       *ptr++ = (unsigned char)(b>>16);
00507     }
00508     
00509   /* copy the first line to the other lines */
00510   for (i=1; i<height; i++)
00511     {
00512       memcpy (&(pixels[i*rowstride]), pixels, rowstride);
00513     }
00514   return pixbuf;
00515 }
00516 
00517 static GdkPixbuf*
00518 meta_gradient_create_multi_vertical (int width, int height,
00519                                      const GdkColor *colors,
00520                                      int count)
00521 {
00522   int i, j, k;
00523   long r, g, b, dr, dg, db;
00524   GdkPixbuf *pixbuf;
00525   unsigned char *ptr, *tmp, *pixels;
00526   int height2;
00527   int x;
00528   int rowstride;
00529   
00530   g_return_val_if_fail (count > 2, NULL);
00531 
00532   pixbuf = blank_pixbuf (width, height, FALSE);
00533   if (pixbuf == NULL)
00534     return NULL;
00535     
00536   pixels = gdk_pixbuf_get_pixels (pixbuf);
00537   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00538   ptr = pixels;
00539     
00540   if (count > height)
00541     count = height;
00542     
00543   if (count > 1)
00544     height2 = height/(count-1);
00545   else
00546     height2 = height;
00547     
00548   k = 0;
00549 
00550   r = colors[0].red << 8;
00551   g = colors[0].green << 8;
00552   b = colors[0].blue << 8;
00553 
00554   for (i=1; i<count; i++)
00555     {
00556       dr = ((int)(colors[i].red   - colors[i-1].red)  <<8)/(int)height2;
00557       dg = ((int)(colors[i].green - colors[i-1].green)<<8)/(int)height2;
00558       db = ((int)(colors[i].blue  - colors[i-1].blue) <<8)/(int)height2;
00559 
00560       for (j=0; j<height2; j++)
00561         {
00562           ptr[0] = (unsigned char)(r>>16);
00563           ptr[1] = (unsigned char)(g>>16);
00564           ptr[2] = (unsigned char)(b>>16);
00565 
00566           for (x=1; x <= width/2; x *= 2)
00567             memcpy (&(ptr[x*3]), ptr, x*3);
00568           memcpy (&(ptr[x*3]), ptr, (width - x)*3);
00569 
00570           ptr += rowstride;
00571           
00572           r += dr;
00573           g += dg;
00574           b += db;
00575           k++;
00576         }
00577       r = colors[i].red << 8;
00578       g = colors[i].green << 8;
00579       b = colors[i].blue << 8;
00580     }
00581 
00582   if (k<height)
00583     {
00584       tmp = ptr;
00585 
00586       ptr[0] = (unsigned char) (r>>16);
00587       ptr[1] = (unsigned char) (g>>16);
00588       ptr[2] = (unsigned char) (b>>16);
00589 
00590       for (x=1; x <= width/2; x *= 2)
00591         memcpy (&(ptr[x*3]), ptr, x*3);
00592       memcpy (&(ptr[x*3]), ptr, (width - x)*3);
00593 
00594       ptr += rowstride;
00595       
00596       for (j=k+1; j<height; j++)
00597         {
00598           memcpy (ptr, tmp, rowstride);
00599           ptr += rowstride;
00600         }
00601     }
00602     
00603   return pixbuf;
00604 }
00605 
00606 
00607 static GdkPixbuf*
00608 meta_gradient_create_multi_diagonal (int width, int height,
00609                                      const GdkColor *colors,
00610                                      int count)
00611 {
00612   GdkPixbuf *pixbuf, *tmp;
00613   float a, offset;
00614   int j;
00615   unsigned char *ptr;
00616   unsigned char *pixels;
00617   int rowstride;
00618   
00619   g_return_val_if_fail (count > 2, NULL);
00620 
00621   if (width == 1)
00622     return meta_gradient_create_multi_vertical (width, height, colors, count);
00623   else if (height == 1)
00624     return meta_gradient_create_multi_horizontal (width, height, colors, count);
00625 
00626   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
00627                            width, height);
00628   if (pixbuf == NULL)
00629     return NULL;
00630     
00631   pixels = gdk_pixbuf_get_pixels (pixbuf);
00632   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00633   
00634   if (count > width)
00635     count = width;
00636   if (count > height)
00637     count = height;
00638 
00639   if (count > 2)
00640     tmp = meta_gradient_create_multi_horizontal (2*width-1, 1, colors, count);
00641   else
00642     /* wrlib multiplies these colors by 256 before passing them in, but
00643      * I think it's a bug in wrlib, so changed here. I could be wrong
00644      * though, if we notice two-color multi diagonals not working.
00645      */
00646     tmp = meta_gradient_create_horizontal (2*width-1, 1,
00647                                            &colors[0], &colors[1]);
00648 
00649   if (!tmp)
00650     {
00651       g_object_unref (G_OBJECT (pixbuf));
00652       return NULL;
00653     }
00654   ptr = gdk_pixbuf_get_pixels (tmp);
00655 
00656   a = ((float)(width - 1))/((float)(height - 1));
00657   width = width * 3;
00658 
00659   /* copy the first line to the other lines with corresponding offset */
00660   for (j=0, offset=0; j<rowstride*height; j += rowstride)
00661     {
00662       memcpy (&(pixels[j]), &ptr[3*(int)offset], width);
00663       offset += a;
00664     }
00665 
00666   g_object_unref (G_OBJECT (tmp));
00667   return pixbuf;
00668 }
00669 
00670 static void
00671 simple_multiply_alpha (GdkPixbuf *pixbuf,
00672                        guchar     alpha)
00673 {
00674   guchar *pixels;
00675   int rowstride;
00676   int height;
00677   int row;
00678 
00679   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
00680   
00681   if (alpha == 255)
00682     return;
00683   
00684   g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
00685   
00686   pixels = gdk_pixbuf_get_pixels (pixbuf);
00687   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00688   height = gdk_pixbuf_get_height (pixbuf);
00689 
00690   row = 0;
00691   while (row < height)
00692     {
00693       guchar *p;
00694       guchar *end;
00695 
00696       p = pixels + row * rowstride;
00697       end = p + rowstride;
00698 
00699       while (p != end)
00700         {
00701           p += 3; /* skip RGB */
00702 
00703           /* multiply the two alpha channels. not sure this is right.
00704            * but some end cases are that if the pixbuf contains 255,
00705            * then it should be modified to contain "alpha"; if the
00706            * pixbuf contains 0, it should remain 0.
00707            */
00708           /* ((*p / 255.0) * (alpha / 255.0)) * 255; */
00709           *p = (guchar) (((int) *p * (int) alpha) / (int) 255);
00710           
00711           ++p; /* skip A */
00712         }
00713 
00714       ++row;
00715     }
00716 }
00717 
00718 static void
00719 meta_gradient_add_alpha_horizontal (GdkPixbuf           *pixbuf,
00720                                     const unsigned char *alphas,
00721                                     int                  n_alphas)
00722 {
00723   int i, j;
00724   long a, da;
00725   unsigned char *p;
00726   unsigned char *pixels;
00727   int width2;  
00728   int rowstride;
00729   int width, height;
00730   unsigned char *gradient;
00731   unsigned char *gradient_p;
00732   unsigned char *gradient_end;
00733   
00734   g_return_if_fail (n_alphas > 0);
00735 
00736   if (n_alphas == 1)
00737     {
00738       /* Optimize this */
00739       simple_multiply_alpha (pixbuf, alphas[0]);
00740       return;
00741     }
00742   
00743   width = gdk_pixbuf_get_width (pixbuf);
00744   height = gdk_pixbuf_get_height (pixbuf);
00745 
00746   gradient = g_new (unsigned char, width);
00747   gradient_end = gradient + width;
00748   
00749   if (n_alphas > width)
00750     n_alphas = width;
00751     
00752   if (n_alphas > 1)
00753     width2 = width / (n_alphas - 1);
00754   else
00755     width2 = width;
00756     
00757   a = alphas[0] << 8;
00758   gradient_p = gradient;
00759   
00760   /* render the gradient into an array */
00761   for (i = 1; i < n_alphas; i++)
00762     {
00763       da = (((int)(alphas[i] - (int) alphas[i-1])) << 8) / (int) width2;
00764 
00765       for (j = 0; j < width2; j++)
00766         {
00767           *gradient_p++ = (a >> 8);
00768           
00769           a += da;
00770         }
00771 
00772       a = alphas[i] << 8;
00773     }
00774 
00775   /* get leftover pixels */
00776   while (gradient_p != gradient_end)
00777     {
00778       *gradient_p++ = a >> 8;
00779     }
00780     
00781   /* Now for each line of the pixbuf, fill in with the gradient */
00782   pixels = gdk_pixbuf_get_pixels (pixbuf);
00783   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00784   
00785   p = pixels;
00786   i = 0;
00787   while (i < height)
00788     {
00789       unsigned char *row_end = p + rowstride;
00790       gradient_p = gradient;
00791 
00792       p += 3;
00793       while (gradient_p != gradient_end)
00794         {
00795           /* multiply the two alpha channels. not sure this is right.
00796            * but some end cases are that if the pixbuf contains 255,
00797            * then it should be modified to contain "alpha"; if the
00798            * pixbuf contains 0, it should remain 0.
00799            */
00800           /* ((*p / 255.0) * (alpha / 255.0)) * 255; */
00801           *p = (guchar) (((int) *p * (int) *gradient_p) / (int) 255);
00802 
00803           p += 4;
00804           ++gradient_p;
00805         }
00806 
00807       p = row_end;
00808       ++i;
00809     }
00810   
00811   g_free (gradient);
00812 }
00813 
00814 void
00815 meta_gradient_add_alpha (GdkPixbuf       *pixbuf,
00816                          const guchar    *alphas,
00817                          int              n_alphas,
00818                          MetaGradientType type)
00819 {
00820   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
00821   g_return_if_fail (gdk_pixbuf_get_has_alpha (pixbuf));
00822   g_return_if_fail (n_alphas > 0);
00823   
00824   switch (type)
00825     {
00826     case META_GRADIENT_HORIZONTAL:
00827       meta_gradient_add_alpha_horizontal (pixbuf, alphas, n_alphas);
00828       break;
00829       
00830     case META_GRADIENT_VERTICAL:
00831       g_printerr ("metacity: vertical alpha channel gradient not implemented yet\n");
00832       break;
00833       
00834     case META_GRADIENT_DIAGONAL:
00835       g_printerr ("metacity: diagonal alpha channel gradient not implemented yet\n");
00836       break;
00837       
00838     case META_GRADIENT_LAST:
00839       g_assert_not_reached ();
00840       break;
00841     }
00842 }

Generated on Sat Aug 23 22:04:18 2008 for metacity by  doxygen 1.5.5