00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "gradient.h"
00026 #include "util.h"
00027 #include <string.h>
00028
00029
00030
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
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
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
00154
00155
00156
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
00248
00249
00250
00251
00252
00253
00254
00255
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
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
00306 for (i=1; i<height; i++)
00307 {
00308 memcpy (&(pixels[i*rowstride]), pixels, rowstride);
00309 }
00310 return pixbuf;
00311 }
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
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
00385
00386
00387
00388
00389
00390
00391
00392
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
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
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
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
00643
00644
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
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;
00702
00703
00704
00705
00706
00707
00708
00709 *p = (guchar) (((int) *p * (int) alpha) / (int) 255);
00710
00711 ++p;
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
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
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
00776 while (gradient_p != gradient_end)
00777 {
00778 *gradient_p++ = a >> 8;
00779 }
00780
00781
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
00796
00797
00798
00799
00800
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 }