- fixed speckles (white dots) on dithered images (bug still present on the
[wmaker-crm.git] / wrlib / gradient.c
blob41516f7ade97dfa53c1ca9fb9e3d1c6b9840319a
1 /* gradient.c - renders gradients
3 * Raster graphics library
5 * Copyright (c) 1997-2000 Alfredo K. Kojima
6 * Copyright (c) 1998-2000 Dan Pascu
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <config.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
29 #include <assert.h>
31 #include "wraster.h"
34 static RImage *renderHGradient(unsigned width, unsigned height,
35 int r0, int g0, int b0,
36 int rf, int gf, int bf);
37 static RImage *renderVGradient(unsigned width, unsigned height,
38 int r0, int g0, int b0,
39 int rf, int gf, int bf);
40 static RImage *renderDGradient(unsigned width, unsigned height,
41 int r0, int g0, int b0,
42 int rf, int gf, int bf);
44 static RImage *renderMHGradient(unsigned width, unsigned height,
45 RColor **colors, int count);
46 static RImage *renderMVGradient(unsigned width, unsigned height,
47 RColor **colors, int count);
48 static RImage *renderMDGradient(unsigned width, unsigned height,
49 RColor **colors, int count);
51 RImage*
52 RRenderMultiGradient(unsigned width, unsigned height, RColor **colors, int style)
54 int count;
56 count = 0;
57 while (colors[count]!=NULL) count++;
59 if (count > 2) {
60 switch (style) {
61 case RHorizontalGradient:
62 return renderMHGradient(width, height, colors, count);
63 case RVerticalGradient:
64 return renderMVGradient(width, height, colors, count);
65 case RDiagonalGradient:
66 return renderMDGradient(width, height, colors, count);
68 } else if (count > 1) {
69 return RRenderGradient(width, height, colors[0], colors[1], style);
70 } else if (count > 0) {
71 return RRenderGradient(width, height, colors[0], colors[0], style);
73 assert(0);
74 return NULL;
79 RImage*
80 RRenderGradient(unsigned width, unsigned height, RColor *from, RColor *to,
81 int style)
83 switch (style) {
84 case RHorizontalGradient:
85 return renderHGradient(width, height, from->red, from->green,
86 from->blue, to->red, to->green, to->blue);
87 case RVerticalGradient:
88 return renderVGradient(width, height, from->red, from->green,
89 from->blue, to->red, to->green, to->blue);
91 case RDiagonalGradient:
92 return renderDGradient(width, height, from->red, from->green,
93 from->blue, to->red, to->green, to->blue);
95 assert(0);
96 return NULL;
101 *----------------------------------------------------------------------
102 * renderHGradient--
103 * Renders a horizontal linear gradient of the specified size in the
104 * RImage format with a border of the specified type.
106 * Returns:
107 * A 24bit RImage with the gradient (no alpha channel).
109 * Side effects:
110 * None
111 *----------------------------------------------------------------------
113 static RImage*
114 renderHGradient(unsigned width, unsigned height, int r0, int g0, int b0,
115 int rf, int gf, int bf)
117 int i;
118 long r, g, b, dr, dg, db;
119 unsigned lineSize = width*3;
120 RImage *image;
121 unsigned char *ptr;
123 image = RCreateImage(width, height, False);
124 if (!image) {
125 return NULL;
127 ptr = image->data;
129 r = r0 << 16;
130 g = g0 << 16;
131 b = b0 << 16;
133 dr = ((rf-r0)<<16)/(int)width;
134 dg = ((gf-g0)<<16)/(int)width;
135 db = ((bf-b0)<<16)/(int)width;
136 /* render the first line */
137 for (i=0; i<width; i++) {
138 *(ptr++) = (unsigned char)(r>>16);
139 *(ptr++) = (unsigned char)(g>>16);
140 *(ptr++) = (unsigned char)(b>>16);
141 r += dr;
142 g += dg;
143 b += db;
146 /* copy the first line to the other lines */
147 for (i=1; i<height; i++) {
148 memcpy(&(image->data[i*lineSize]), image->data, lineSize);
150 return image;
156 *----------------------------------------------------------------------
157 * renderVGradient--
158 * Renders a vertical linear gradient of the specified size in the
159 * RImage format with a border of the specified type.
161 * Returns:
162 * A 24bit RImage with the gradient (no alpha channel).
164 * Side effects:
165 * None
166 *----------------------------------------------------------------------
168 static RImage*
169 renderVGradient(unsigned width, unsigned height, int r0, int g0, int b0,
170 int rf, int gf, int bf)
172 int i, j;
173 long r, g, b, dr, dg, db;
174 RImage *image;
175 unsigned char *ptr;
176 unsigned char rr, gg, bb;
178 image = RCreateImage(width, height, False);
179 if (!image) {
180 return NULL;
182 ptr = image->data;
184 r = r0<<16;
185 g = g0<<16;
186 b = b0<<16;
188 dr = ((rf-r0)<<16)/(int)height;
189 dg = ((gf-g0)<<16)/(int)height;
190 db = ((bf-b0)<<16)/(int)height;
192 for (i=0; i<height; i++) {
193 rr = r>>16;
194 gg = g>>16;
195 bb = b>>16;
196 for (j=0; j<width/8; j++) {
197 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
198 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
199 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
200 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
201 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
202 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
203 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
204 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
206 switch (width%8) {
207 case 7: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
208 case 6: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
209 case 5: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
210 case 4: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
211 case 3: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
212 case 2: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
213 case 1: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
215 r+=dr;
216 g+=dg;
217 b+=db;
219 return image;
224 *----------------------------------------------------------------------
225 * renderDGradient--
226 * Renders a diagonal linear gradient of the specified size in the
227 * RImage format with a border of the specified type.
229 * Returns:
230 * A 24bit RImage with the gradient (no alpha channel).
232 * Side effects:
233 * None
234 *----------------------------------------------------------------------
238 static RImage*
239 renderDGradient(unsigned width, unsigned height, int r0, int g0, int b0,
240 int rf, int gf, int bf)
242 RImage *image, *tmp;
243 int j;
244 float a, offset;
245 char *ptr;
247 if (width == 1)
248 return renderVGradient(width, height, r0, g0, b0, rf, gf, bf);
249 else if (height == 1)
250 return renderHGradient(width, height, r0, g0, b0, rf, gf, bf);
252 image = RCreateImage(width, height, False);
253 if (!image) {
254 return NULL;
257 tmp = renderHGradient(2*width-1, 1, r0, g0, b0, rf, gf, bf);
258 if (!tmp) {
259 RReleaseImage(image);
260 return NULL;
263 ptr = tmp->data;
265 a = ((float)(width - 1))/((float)(height - 1));
266 width = width * 3;
268 /* copy the first line to the other lines with corresponding offset */
269 for (j=0, offset=0.0; j<width*height; j += width) {
270 memcpy(&(image->data[j]), &ptr[3*(int)offset], width);
271 offset += a;
274 RReleaseImage(tmp);
275 return image;
279 static RImage*
280 renderMHGradient(unsigned width, unsigned height, RColor **colors, int count)
282 int i, j, k;
283 long r, g, b, dr, dg, db;
284 unsigned lineSize = width*3;
285 RImage *image;
286 unsigned char *ptr;
287 unsigned width2;
290 assert(count > 2);
292 image = RCreateImage(width, height, False);
293 if (!image) {
294 return NULL;
296 ptr = image->data;
298 if (count > width)
299 count = width;
301 if (count > 1)
302 width2 = width/(count-1);
303 else
304 width2 = width;
306 k = 0;
308 r = colors[0]->red << 16;
309 g = colors[0]->green << 16;
310 b = colors[0]->blue << 16;
312 /* render the first line */
313 for (i=1; i<count; i++) {
314 dr = ((int)(colors[i]->red - colors[i-1]->red) <<16)/(int)width2;
315 dg = ((int)(colors[i]->green - colors[i-1]->green)<<16)/(int)width2;
316 db = ((int)(colors[i]->blue - colors[i-1]->blue) <<16)/(int)width2;
317 for (j=0; j<width2; j++) {
318 *ptr++ = (unsigned char)(r>>16);
319 *ptr++ = (unsigned char)(g>>16);
320 *ptr++ = (unsigned char)(b>>16);
321 r += dr;
322 g += dg;
323 b += db;
324 k++;
326 r = colors[i]->red << 16;
327 g = colors[i]->green << 16;
328 b = colors[i]->blue << 16;
330 for (j=k; j<width; j++) {
331 *ptr++ = (unsigned char)(r>>16);
332 *ptr++ = (unsigned char)(g>>16);
333 *ptr++ = (unsigned char)(b>>16);
336 /* copy the first line to the other lines */
337 for (i=1; i<height; i++) {
338 memcpy(&(image->data[i*lineSize]), image->data, lineSize);
340 return image;
346 static RImage*
347 renderMVGradient(unsigned width, unsigned height, RColor **colors, int count)
349 int i, j, k;
350 long r, g, b, dr, dg, db;
351 unsigned lineSize = width*3;
352 RImage *image;
353 unsigned char *ptr, *tmp;
354 unsigned height2;
355 int x;
356 unsigned char rr, gg, bb;
359 assert(count > 2);
361 image = RCreateImage(width, height, False);
362 if (!image) {
363 return NULL;
365 ptr = image->data;
367 if (count > height)
368 count = height;
370 if (count > 1)
371 height2 = height/(count-1);
372 else
373 height2 = height;
375 k = 0;
377 r = colors[0]->red << 16;
378 g = colors[0]->green << 16;
379 b = colors[0]->blue << 16;
381 for (i=1; i<count; i++) {
382 dr = ((int)(colors[i]->red - colors[i-1]->red) <<16)/(int)height2;
383 dg = ((int)(colors[i]->green - colors[i-1]->green)<<16)/(int)height2;
384 db = ((int)(colors[i]->blue - colors[i-1]->blue) <<16)/(int)height2;
386 for (j=0; j<height2; j++) {
387 rr = r>>16;
388 gg = g>>16;
389 bb = b>>16;
391 for (x=0; x<width/4; x++) {
392 *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
393 *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
394 *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
395 *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
397 switch (width%4) {
398 case 3: *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
399 case 2: *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
400 case 1: *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
402 r += dr;
403 g += dg;
404 b += db;
405 k++;
407 r = colors[i]->red << 16;
408 g = colors[i]->green << 16;
409 b = colors[i]->blue << 16;
412 rr = r>>16;
413 gg = g>>16;
414 bb = b>>16;
416 if (k<height) {
417 tmp = ptr;
418 for (x=0; x<width/4; x++) {
419 *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
420 *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
421 *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
422 *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
424 switch (width%4) {
425 case 3: *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
426 case 2: *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
427 case 1: *ptr++ = rr; *ptr++ = gg; *ptr++ = bb;
428 default: break;
431 for (j=k+1; j<height; j++) {
432 memcpy(ptr, tmp, lineSize);
433 ptr += lineSize;
437 return image;
441 static RImage*
442 renderMDGradient(unsigned width, unsigned height, RColor **colors, int count)
444 RImage *image, *tmp;
445 float a, offset;
446 int j;
447 unsigned char *ptr;
449 assert(count > 2);
451 if (width == 1)
452 return renderMVGradient(width, height, colors, count);
453 else if (height == 1)
454 return renderMHGradient(width, height, colors, count);
456 image = RCreateImage(width, height, False);
457 if (!image) {
458 return NULL;
461 if (count > width)
462 count = width;
463 if (count > height)
464 count = height;
466 if (count > 2)
467 tmp = renderMHGradient(2*width-1, 1, colors, count);
468 else
469 tmp = renderHGradient(2*width-1, 1, colors[0]->red<<8,
470 colors[0]->green<<8, colors[0]->blue<<8,
471 colors[1]->red<<8, colors[1]->green<<8,
472 colors[1]->blue<<8);
474 if (!tmp) {
475 RReleaseImage(image);
476 return NULL;
478 ptr = tmp->data;
480 a = ((float)(width - 1))/((float)(height - 1));
481 width = width * 3;
483 /* copy the first line to the other lines with corresponding offset */
484 for (j=0, offset=0; j<width*height; j += width) {
485 memcpy(&(image->data[j]), &ptr[3*(int)offset], width);
486 offset += a;
488 RReleaseImage(tmp);
489 return image;
495 RImage*
496 RRenderInterwovenGradient(unsigned width, unsigned height,
497 RColor colors1[2], int thickness1,
498 RColor colors2[2], int thickness2)
500 int i, j, k, l, ll;
501 long r1, g1, b1, dr1, dg1, db1;
502 long r2, g2, b2, dr2, dg2, db2;
503 RImage *image;
504 unsigned char *ptr;
505 unsigned char rr, gg, bb;
507 image = RCreateImage(width, height, False);
508 if (!image) {
509 return NULL;
511 ptr = image->data;
513 r1 = colors1[0].red<<16;
514 g1 = colors1[0].green<<16;
515 b1 = colors1[0].blue<<16;
517 r2 = colors2[0].red<<16;
518 g2 = colors2[0].green<<16;
519 b2 = colors2[0].blue<<16;
521 dr1 = ((colors1[1].red-colors1[0].red)<<16)/(int)height;
522 dg1 = ((colors1[1].green-colors1[0].green)<<16)/(int)height;
523 db1 = ((colors1[1].blue-colors1[0].blue)<<16)/(int)height;
525 dr2 = ((colors2[1].red-colors2[0].red)<<16)/(int)height;
526 dg2 = ((colors2[1].green-colors2[0].green)<<16)/(int)height;
527 db2 = ((colors2[1].blue-colors2[0].blue)<<16)/(int)height;
529 for (i=0,k=0,l=0,ll=thickness1; i<height; i++) {
530 if (k == 0) {
531 rr = r1>>16;
532 gg = g1>>16;
533 bb = b1>>16;
534 } else {
535 rr = r2>>16;
536 gg = g2>>16;
537 bb = b2>>16;
539 for (j=0; j<width/8; j++) {
540 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
541 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
542 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
543 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
544 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
545 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
546 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
547 *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
549 switch (width%8) {
550 case 7: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
551 case 6: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
552 case 5: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
553 case 4: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
554 case 3: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
555 case 2: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
556 case 1: *(ptr++) = rr; *(ptr++) = gg; *(ptr++) = bb;
558 if (++l == ll) {
559 if (k == 0) {
560 k = 1;
561 ll = thickness2;
562 } else {
563 k = 0;
564 ll = thickness1;
566 l = 0;
568 r1+=dr1;
569 g1+=dg1;
570 b1+=db1;
572 r2+=dr2;
573 g2+=dg2;
574 b2+=db2;
576 return image;