WINGs: reuse GNUstep header instead of duplicating stuff in wwindow.c
[wmaker-crm.git] / wrlib / convert.c
blob23df5c6663faab2cba9c1e96278bcc1d43f142d8
1 /* convert.c - convert RImage to Pixmap
3 * Raster graphics library
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
23 /* Problems:
24 * 1. Using Grayscale visual with Dithering crashes wmaker
25 * 2. Ghost dock/appicon is wrong in Pseudocolor, Staticgray, Grayscale
28 #include <config.h>
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <assert.h>
36 #include "wraster.h"
37 #include "convert.h"
38 #include "xutil.h"
41 #define NFREE(n) if (n) free(n)
43 #define HAS_ALPHA(I) ((I)->format == RRGBAFormat)
45 typedef struct RConversionTable {
46 unsigned short table[256];
47 unsigned short index;
49 struct RConversionTable *next;
50 } RConversionTable;
52 typedef struct RStdConversionTable {
53 unsigned int table[256];
55 unsigned short mult;
56 unsigned short max;
58 struct RStdConversionTable *next;
59 } RStdConversionTable;
61 static RConversionTable *conversionTable = NULL;
62 static RStdConversionTable *stdConversionTable = NULL;
64 static void release_conversion_table(void)
66 RConversionTable *tmp = conversionTable;
68 while (tmp) {
69 RConversionTable *tmp_to_delete = tmp;
71 tmp = tmp->next;
72 free(tmp_to_delete);
74 conversionTable = NULL;
77 static void release_std_conversion_table(void)
79 RStdConversionTable *tmp = stdConversionTable;
81 while (tmp) {
82 RStdConversionTable *tmp_to_delete = tmp;
84 tmp = tmp->next;
85 free(tmp_to_delete);
87 stdConversionTable = NULL;
90 void r_destroy_conversion_tables(void)
92 release_conversion_table();
93 release_std_conversion_table();
96 static unsigned short *computeTable(unsigned short mask)
98 RConversionTable *tmp = conversionTable;
99 int i;
101 while (tmp) {
102 if (tmp->index == mask)
103 break;
104 tmp = tmp->next;
107 if (tmp)
108 return tmp->table;
110 tmp = (RConversionTable *) malloc(sizeof(RConversionTable));
111 if (tmp == NULL)
112 return NULL;
114 for (i = 0; i < 256; i++)
115 tmp->table[i] = (i * mask + 0x7f) / 0xff;
117 tmp->index = mask;
118 tmp->next = conversionTable;
119 conversionTable = tmp;
120 return tmp->table;
123 static unsigned int *computeStdTable(unsigned int mult, unsigned int max)
125 RStdConversionTable *tmp = stdConversionTable;
126 unsigned int i;
128 while (tmp) {
129 if (tmp->mult == mult && tmp->max == max)
130 break;
131 tmp = tmp->next;
134 if (tmp)
135 return tmp->table;
137 tmp = (RStdConversionTable *) malloc(sizeof(RStdConversionTable));
138 if (tmp == NULL)
139 return NULL;
141 for (i = 0; i < 256; i++) {
142 tmp->table[i] = (i * max) / 0xff * mult;
144 tmp->mult = mult;
145 tmp->max = max;
147 tmp->next = stdConversionTable;
148 stdConversionTable = tmp;
150 return tmp->table;
153 /***************************************************************************/
155 static void
156 convertTrueColor_generic(RXImage * ximg, RImage * image,
157 signed char *err, signed char *nerr,
158 const unsigned short *rtable,
159 const unsigned short *gtable,
160 const unsigned short *btable,
161 const int dr, const int dg, const int db,
162 const unsigned short roffs, const unsigned short goffs, const unsigned short boffs)
164 signed char *terr;
165 int x, y, r, g, b;
166 int pixel;
167 int rer, ger, ber;
168 unsigned char *ptr = image->data;
169 int channels = (HAS_ALPHA(image) ? 4 : 3);
171 /* convert and dither the image to XImage */
172 for (y = 0; y < image->height; y++) {
173 nerr[0] = 0;
174 nerr[1] = 0;
175 nerr[2] = 0;
176 for (x = 0; x < image->width; x++, ptr += channels) {
178 /* reduce pixel */
179 pixel = *ptr + err[x];
180 if (pixel < 0)
181 pixel = 0;
182 else if (pixel > 0xff)
183 pixel = 0xff;
184 r = rtable[pixel];
185 /* calc error */
186 rer = pixel - r * dr;
188 /* reduce pixel */
189 pixel = *(ptr + 1) + err[x + 1];
190 if (pixel < 0)
191 pixel = 0;
192 else if (pixel > 0xff)
193 pixel = 0xff;
194 g = gtable[pixel];
195 /* calc error */
196 ger = pixel - g * dg;
198 /* reduce pixel */
199 pixel = *(ptr + 2) + err[x + 2];
200 if (pixel < 0)
201 pixel = 0;
202 else if (pixel > 0xff)
203 pixel = 0xff;
204 b = btable[pixel];
205 /* calc error */
206 ber = pixel - b * db;
208 pixel = (r << roffs) | (g << goffs) | (b << boffs);
209 XPutPixel(ximg->image, x, y, pixel);
211 /* distribute error */
212 r = (rer * 3) / 8;
213 g = (ger * 3) / 8;
214 b = (ber * 3) / 8;
215 /* x+1, y */
216 err[x + 3 * 1] += r;
217 err[x + 1 + 3 * 1] += g;
218 err[x + 2 + 3 * 1] += b;
219 /* x, y+1 */
220 nerr[x] += r;
221 nerr[x + 1] += g;
222 nerr[x + 2] += b;
223 /* x+1, y+1 */
224 nerr[x + 3 * 1] = rer - 2 * r;
225 nerr[x + 1 + 3 * 1] = ger - 2 * g;
226 nerr[x + 2 + 3 * 1] = ber - 2 * b;
228 /* skip to next line */
229 terr = err;
230 err = nerr;
231 nerr = terr;
234 /* redither the 1st line to distribute error better */
235 ptr = image->data;
236 y = 0;
237 nerr[0] = 0;
238 nerr[1] = 0;
239 nerr[2] = 0;
240 for (x = 0; x < image->width; x++, ptr += channels) {
242 /* reduce pixel */
243 pixel = *ptr + err[x];
244 if (pixel < 0)
245 pixel = 0;
246 else if (pixel > 0xff)
247 pixel = 0xff;
248 r = rtable[pixel];
249 /* calc error */
250 rer = pixel - r * dr;
252 /* reduce pixel */
253 pixel = *(ptr + 1) + err[x + 1];
254 if (pixel < 0)
255 pixel = 0;
256 else if (pixel > 0xff)
257 pixel = 0xff;
258 g = gtable[pixel];
259 /* calc error */
260 ger = pixel - g * dg;
262 /* reduce pixel */
263 pixel = *(ptr + 2) + err[x + 2];
264 if (pixel < 0)
265 pixel = 0;
266 else if (pixel > 0xff)
267 pixel = 0xff;
268 b = btable[pixel];
269 /* calc error */
270 ber = pixel - b * db;
272 pixel = (r << roffs) | (g << goffs) | (b << boffs);
273 XPutPixel(ximg->image, x, y, pixel);
275 /* distribute error */
276 r = (rer * 3) / 8;
277 g = (ger * 3) / 8;
278 b = (ber * 3) / 8;
279 /* x+1, y */
280 err[x + 3 * 1] += r;
281 err[x + 1 + 3 * 1] += g;
282 err[x + 2 + 3 * 1] += b;
283 /* x, y+1 */
284 nerr[x] += r;
285 nerr[x + 1] += g;
286 nerr[x + 2] += b;
287 /* x+1, y+1 */
288 nerr[x + 3 * 1] = rer - 2 * r;
289 nerr[x + 1 + 3 * 1] = ger - 2 * g;
290 nerr[x + 2 + 3 * 1] = ber - 2 * b;
294 static RXImage *image2TrueColor(RContext * ctx, RImage * image)
296 RXImage *ximg;
297 unsigned short rmask, gmask, bmask;
298 unsigned short roffs, goffs, boffs;
299 unsigned short *rtable, *gtable, *btable;
300 int channels = (HAS_ALPHA(image) ? 4 : 3);
302 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
303 if (!ximg) {
304 return NULL;
307 roffs = ctx->red_offset;
308 goffs = ctx->green_offset;
309 boffs = ctx->blue_offset;
311 rmask = ctx->visual->red_mask >> roffs;
312 gmask = ctx->visual->green_mask >> goffs;
313 bmask = ctx->visual->blue_mask >> boffs;
315 rtable = computeTable(rmask);
316 gtable = computeTable(gmask);
317 btable = computeTable(bmask);
319 if (rtable == NULL || gtable == NULL || btable == NULL) {
320 RErrorCode = RERR_NOMEMORY;
321 RDestroyXImage(ctx, ximg);
322 return NULL;
325 if (ctx->attribs->render_mode == RBestMatchRendering) {
326 int ofs;
327 unsigned long r, g, b;
328 int x, y;
329 unsigned long pixel;
330 unsigned char *ptr = image->data;
332 /* fake match */
333 #ifdef WRLIB_DEBUG
334 fputs("true color match\n", stderr);
335 #endif
336 if (rmask == 0xff && gmask == 0xff && bmask == 0xff) {
337 for (y = 0; y < image->height; y++) {
338 for (x = 0; x < image->width; x++, ptr += channels) {
339 /* reduce pixel */
340 r = ptr[0];
341 g = ptr[1];
342 b = ptr[2];
343 pixel = (r << roffs) | (g << goffs) | (b << boffs);
344 XPutPixel(ximg->image, x, y, pixel);
347 } else {
348 for (y = 0, ofs = 0; y < image->height; y++) {
349 for (x = 0; x < image->width; x++, ofs += channels - 3) {
350 /* reduce pixel */
351 r = rtable[ptr[ofs++]];
352 g = gtable[ptr[ofs++]];
353 b = btable[ptr[ofs++]];
354 pixel = (r << roffs) | (g << goffs) | (b << boffs);
355 XPutPixel(ximg->image, x, y, pixel);
359 } else {
360 /* dither */
361 const int dr = 0xff / rmask;
362 const int dg = 0xff / gmask;
363 const int db = 0xff / bmask;
365 #ifdef WRLIB_DEBUG
366 fputs("true color dither\n", stderr);
367 #endif
370 signed char *err;
371 signed char *nerr;
372 int ch = (HAS_ALPHA(image) ? 4 : 3);
374 err = malloc(ch * (image->width + 2));
375 nerr = malloc(ch * (image->width + 2));
376 if (!err || !nerr) {
377 NFREE(err);
378 NFREE(nerr);
379 RErrorCode = RERR_NOMEMORY;
380 RDestroyXImage(ctx, ximg);
381 return NULL;
384 memset(err, 0, ch * (image->width + 2));
385 memset(nerr, 0, ch * (image->width + 2));
387 convertTrueColor_generic(ximg, image, err, nerr,
388 rtable, gtable, btable, dr, dg, db, roffs, goffs, boffs);
389 free(err);
390 free(nerr);
395 return ximg;
398 /***************************************************************************/
400 static void
401 convertPseudoColor_to_8(RXImage * ximg, RImage * image,
402 signed char *err, signed char *nerr,
403 const unsigned short *rtable,
404 const unsigned short *gtable,
405 const unsigned short *btable,
406 const int dr, const int dg, const int db, unsigned long *pixels, int cpc)
408 signed char *terr;
409 int x, y, r, g, b;
410 int pixel;
411 int rer, ger, ber;
412 unsigned char *ptr = image->data;
413 unsigned char *optr = (unsigned char *)ximg->image->data;
414 int channels = (HAS_ALPHA(image) ? 4 : 3);
415 int cpcpc = cpc * cpc;
417 /* convert and dither the image to XImage */
418 for (y = 0; y < image->height; y++) {
419 nerr[0] = 0;
420 nerr[1] = 0;
421 nerr[2] = 0;
422 for (x = 0; x < image->width * 3; x += 3, ptr += channels) {
424 /* reduce pixel */
425 pixel = *ptr + err[x];
426 if (pixel < 0)
427 pixel = 0;
428 else if (pixel > 0xff)
429 pixel = 0xff;
430 r = rtable[pixel];
431 /* calc error */
432 rer = pixel - r * dr;
434 /* reduce pixel */
435 pixel = *(ptr + 1) + err[x + 1];
436 if (pixel < 0)
437 pixel = 0;
438 else if (pixel > 0xff)
439 pixel = 0xff;
440 g = gtable[pixel];
441 /* calc error */
442 ger = pixel - g * dg;
444 /* reduce pixel */
445 pixel = *(ptr + 2) + err[x + 2];
446 if (pixel < 0)
447 pixel = 0;
448 else if (pixel > 0xff)
449 pixel = 0xff;
450 b = btable[pixel];
451 /* calc error */
452 ber = pixel - b * db;
454 *optr++ = pixels[r * cpcpc + g * cpc + b];
456 /* distribute error */
457 r = (rer * 3) / 8;
458 g = (ger * 3) / 8;
459 b = (ber * 3) / 8;
461 /* x+1, y */
462 err[x + 3 * 1] += r;
463 err[x + 1 + 3 * 1] += g;
464 err[x + 2 + 3 * 1] += b;
465 /* x, y+1 */
466 nerr[x] += r;
467 nerr[x + 1] += g;
468 nerr[x + 2] += b;
469 /* x+1, y+1 */
470 nerr[x + 3 * 1] = rer - 2 * r;
471 nerr[x + 1 + 3 * 1] = ger - 2 * g;
472 nerr[x + 2 + 3 * 1] = ber - 2 * b;
474 /* skip to next line */
475 terr = err;
476 err = nerr;
477 nerr = terr;
479 optr += ximg->image->bytes_per_line - image->width;
483 static RXImage *image2PseudoColor(RContext * ctx, RImage * image)
485 RXImage *ximg;
486 register int x, y, r, g, b;
487 unsigned char *ptr;
488 unsigned long pixel;
489 const int cpc = ctx->attribs->colors_per_channel;
490 const unsigned short rmask = cpc - 1; /* different sizes could be used */
491 const unsigned short gmask = rmask; /* for r,g,b */
492 const unsigned short bmask = rmask;
493 unsigned short *rtable, *gtable, *btable;
494 const int cpccpc = cpc * cpc;
495 int channels = (HAS_ALPHA(image) ? 4 : 3);
497 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
498 if (!ximg) {
499 return NULL;
502 ptr = image->data;
504 /* Tables are same at the moment because rmask==gmask==bmask. */
505 rtable = computeTable(rmask);
506 gtable = computeTable(gmask);
507 btable = computeTable(bmask);
509 if (rtable == NULL || gtable == NULL || btable == NULL) {
510 RErrorCode = RERR_NOMEMORY;
511 RDestroyXImage(ctx, ximg);
512 return NULL;
515 if (ctx->attribs->render_mode == RBestMatchRendering) {
516 /* fake match */
517 #ifdef WRLIB_DEBUG
518 fprintf(stderr, "pseudo color match with %d colors per channel\n", cpc);
519 #endif
520 for (y = 0; y < image->height; y++) {
521 for (x = 0; x < image->width; x++, ptr += channels - 3) {
522 /* reduce pixel */
523 r = rtable[*ptr++];
524 g = gtable[*ptr++];
525 b = btable[*ptr++];
526 pixel = r * cpccpc + g * cpc + b;
527 /*data[ofs] = ctx->colors[pixel].pixel; */
528 XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel);
531 } else {
532 /* dither */
533 signed char *err;
534 signed char *nerr;
535 const int dr = 0xff / rmask;
536 const int dg = 0xff / gmask;
537 const int db = 0xff / bmask;
539 #ifdef WRLIB_DEBUG
540 fprintf(stderr, "pseudo color dithering with %d colors per channel\n", cpc);
541 #endif
542 err = malloc(4 * (image->width + 3));
543 nerr = malloc(4 * (image->width + 3));
544 if (!err || !nerr) {
545 NFREE(err);
546 NFREE(nerr);
547 RErrorCode = RERR_NOMEMORY;
548 RDestroyXImage(ctx, ximg);
549 return NULL;
551 memset(err, 0, 4 * (image->width + 3));
552 memset(nerr, 0, 4 * (image->width + 3));
554 convertPseudoColor_to_8(ximg, image, err + 4, nerr + 4,
555 rtable, gtable, btable, dr, dg, db, ctx->pixels, cpc);
557 free(err);
558 free(nerr);
561 return ximg;
565 * For standard colormap
567 static RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image)
569 RXImage *ximg;
570 register int x, y, r, g, b;
571 unsigned char *ptr;
572 unsigned long pixel;
573 unsigned char *data;
574 unsigned int *rtable, *gtable, *btable;
575 unsigned int base_pixel = ctx->std_rgb_map->base_pixel;
576 int channels = (HAS_ALPHA(image) ? 4 : 3);
578 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
579 if (!ximg) {
580 return NULL;
583 ptr = image->data;
585 data = (unsigned char *)ximg->image->data;
587 rtable = computeStdTable(ctx->std_rgb_map->red_mult, ctx->std_rgb_map->red_max);
589 gtable = computeStdTable(ctx->std_rgb_map->green_mult, ctx->std_rgb_map->green_max);
591 btable = computeStdTable(ctx->std_rgb_map->blue_mult, ctx->std_rgb_map->blue_max);
593 if (rtable == NULL || gtable == NULL || btable == NULL) {
594 RErrorCode = RERR_NOMEMORY;
595 RDestroyXImage(ctx, ximg);
596 return NULL;
599 if (ctx->attribs->render_mode == RBestMatchRendering) {
600 for (y = 0; y < image->height; y++) {
601 for (x = 0; x < image->width; x++, ptr += channels) {
602 /* reduce pixel */
603 pixel = (rtable[*ptr] + gtable[*(ptr + 1)]
604 + btable[*(ptr + 2)] + base_pixel) & 0xffffffff;
606 XPutPixel(ximg->image, x, y, pixel);
609 } else {
610 /* dither */
611 signed short *err, *nerr;
612 signed short *terr;
613 int rer, ger, ber;
614 int x1, ofs;
616 #ifdef WRLIB_DEBUG
617 fprintf(stderr, "pseudo color dithering with %d colors per channel\n",
618 ctx->attribs->colors_per_channel);
619 #endif
620 err = (short *)malloc(3 * (image->width + 2) * sizeof(short));
621 nerr = (short *)malloc(3 * (image->width + 2) * sizeof(short));
622 if (!err || !nerr) {
623 NFREE(err);
624 NFREE(nerr);
625 RErrorCode = RERR_NOMEMORY;
626 RDestroyXImage(ctx, ximg);
627 return NULL;
629 for (x = 0, x1 = 0; x < image->width * 3; x1 += channels - 3) {
630 err[x++] = ptr[x1++];
631 err[x++] = ptr[x1++];
632 err[x++] = ptr[x1++];
634 err[x] = err[x + 1] = err[x + 2] = 0;
635 /* convert and dither the image to XImage */
636 for (y = 0, ofs = 0; y < image->height; y++) {
637 if (y < image->height - 1) {
638 int x1;
639 for (x = 0, x1 = (y + 1) * image->width * channels;
640 x < image->width * 3; x1 += channels - 3) {
641 nerr[x++] = ptr[x1++];
642 nerr[x++] = ptr[x1++];
643 nerr[x++] = ptr[x1++];
645 /* last column */
646 x1 -= channels;
647 nerr[x++] = ptr[x1++];
648 nerr[x++] = ptr[x1++];
649 nerr[x++] = ptr[x1++];
651 for (x = 0; x < image->width * 3; x += 3, ofs++) {
652 /* reduce pixel */
653 if (err[x] > 0xff)
654 err[x] = 0xff;
655 else if (err[x] < 0)
656 err[x] = 0;
657 if (err[x + 1] > 0xff)
658 err[x + 1] = 0xff;
659 else if (err[x + 1] < 0)
660 err[x + 1] = 0;
661 if (err[x + 2] > 0xff)
662 err[x + 2] = 0xff;
663 else if (err[x + 2] < 0)
664 err[x + 2] = 0;
666 r = rtable[err[x]];
667 g = gtable[err[x + 1]];
668 b = btable[err[x + 2]];
670 pixel = r + g + b;
672 data[ofs] = base_pixel + pixel;
674 /* calc error */
675 rer = err[x] - (ctx->colors[pixel].red >> 8);
676 ger = err[x + 1] - (ctx->colors[pixel].green >> 8);
677 ber = err[x + 2] - (ctx->colors[pixel].blue >> 8);
679 /* distribute error */
680 err[x + 3 * 1] += (rer * 7) / 16;
681 err[x + 1 + 3 * 1] += (ger * 7) / 16;
682 err[x + 2 + 3 * 1] += (ber * 7) / 16;
684 nerr[x] += (rer * 5) / 16;
685 nerr[x + 1] += (ger * 5) / 16;
686 nerr[x + 2] += (ber * 5) / 16;
688 if (x > 0) {
689 nerr[x - 3 * 1] += (rer * 3) / 16;
690 nerr[x - 3 * 1 + 1] += (ger * 3) / 16;
691 nerr[x - 3 * 1 + 2] += (ber * 3) / 16;
694 nerr[x + 3 * 1] += rer / 16;
695 nerr[x + 1 + 3 * 1] += ger / 16;
696 nerr[x + 2 + 3 * 1] += ber / 16;
698 /* skip to next line */
699 terr = err;
700 err = nerr;
701 nerr = terr;
703 ofs += ximg->image->bytes_per_line - image->width;
705 free(err);
706 free(nerr);
708 ximg->image->data = (char *)data;
710 return ximg;
713 static RXImage *image2GrayScale(RContext * ctx, RImage * image)
715 RXImage *ximg;
716 register int x, y, g;
717 unsigned char *ptr;
718 const int cpc = ctx->attribs->colors_per_channel;
719 unsigned short gmask;
720 unsigned short *table;
721 unsigned char *data;
722 int channels = (HAS_ALPHA(image) ? 4 : 3);
724 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
725 if (!ximg) {
726 return NULL;
729 ptr = image->data;
731 data = (unsigned char *)ximg->image->data;
733 if (ctx->vclass == StaticGray)
734 gmask = (1 << ctx->depth) - 1; /* use all grays */
735 else
736 gmask = cpc * cpc * cpc - 1;
738 table = computeTable(gmask);
740 if (table == NULL) {
741 RErrorCode = RERR_NOMEMORY;
742 RDestroyXImage(ctx, ximg);
743 return NULL;
746 if (ctx->attribs->render_mode == RBestMatchRendering) {
747 /* fake match */
748 #ifdef WRLIB_DEBUG
749 fprintf(stderr, "grayscale match with %d colors per channel\n", cpc);
750 #endif
751 for (y = 0; y < image->height; y++) {
752 for (x = 0; x < image->width; x++) {
753 /* reduce pixel */
754 g = table[(*ptr * 30 + *(ptr + 1) * 59 + *(ptr + 2) * 11) / 100];
755 ptr += channels;
756 /*data[ofs] = ctx->colors[g].pixel; */
757 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
760 } else {
761 /* dither */
762 short *gerr;
763 short *ngerr;
764 short *terr;
765 int ger;
766 const int dg = 0xff / gmask;
768 #ifdef WRLIB_DEBUG
769 fprintf(stderr, "grayscale dither with %d colors per channel\n", cpc);
770 #endif
771 gerr = (short *)malloc((image->width + 2) * sizeof(short));
772 ngerr = (short *)malloc((image->width + 2) * sizeof(short));
773 if (!gerr || !ngerr) {
774 NFREE(gerr);
775 NFREE(ngerr);
776 RErrorCode = RERR_NOMEMORY;
777 RDestroyXImage(ctx, ximg);
778 return NULL;
780 for (x = 0, y = 0; x < image->width; x++, y += channels) {
781 gerr[x] = (ptr[y] * 30 + ptr[y + 1] * 59 + ptr[y + 2] * 11) / 100;
783 gerr[x] = 0;
784 /* convert and dither the image to XImage */
785 for (y = 0; y < image->height; y++) {
786 if (y < image->height - 1) {
787 int x1;
788 for (x = 0, x1 = (y + 1) * image->width * channels; x < image->width;
789 x++, x1 += channels) {
790 ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100;
792 /* last column */
793 x1 -= channels;
794 ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100;
796 for (x = 0; x < image->width; x++) {
797 /* reduce pixel */
798 if (gerr[x] > 0xff)
799 gerr[x] = 0xff;
800 else if (gerr[x] < 0)
801 gerr[x] = 0;
803 g = table[gerr[x]];
805 /*data[ofs] = ctx->colors[g].pixel; */
806 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
807 /* calc error */
808 ger = gerr[x] - g * dg;
810 /* distribute error */
811 g = (ger * 3) / 8;
812 /* x+1, y */
813 gerr[x + 1] += g;
814 /* x, y+1 */
815 ngerr[x] += g;
816 /* x+1, y+1 */
817 ngerr[x + 1] += ger - 2 * g;
819 /* skip to next line */
820 terr = gerr;
821 gerr = ngerr;
822 ngerr = terr;
824 free(gerr);
825 free(ngerr);
827 ximg->image->data = (char *)data;
829 return ximg;
832 static RXImage *image2Bitmap(RContext * ctx, RImage * image, int threshold)
834 RXImage *ximg;
835 unsigned char *alpha;
836 int x, y;
838 ximg = RCreateXImage(ctx, 1, image->width, image->height);
839 if (!ximg) {
840 return NULL;
842 alpha = image->data + 3;
844 for (y = 0; y < image->height; y++) {
845 for (x = 0; x < image->width; x++) {
846 XPutPixel(ximg->image, x, y, (*alpha <= threshold ? 0 : 1));
847 alpha += 4;
851 return ximg;
854 int RConvertImage(RContext * context, RImage * image, Pixmap * pixmap)
856 RXImage *ximg = NULL;
857 #ifdef USE_XSHM
858 Pixmap tmp;
859 #endif
861 assert(context != NULL);
862 assert(image != NULL);
863 assert(pixmap != NULL);
865 switch (context->vclass) {
866 case TrueColor:
867 ximg = image2TrueColor(context, image);
868 break;
870 case PseudoColor:
871 case StaticColor:
872 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap)
873 ximg = image2StandardPseudoColor(context, image);
874 else
875 ximg = image2PseudoColor(context, image);
876 break;
878 case GrayScale:
879 case StaticGray:
880 ximg = image2GrayScale(context, image);
881 break;
884 if (!ximg) {
885 return False;
888 *pixmap = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, context->depth);
890 #ifdef USE_XSHM
891 if (context->flags.use_shared_pixmap && ximg->is_shared)
892 tmp = R_CreateXImageMappedPixmap(context, ximg);
893 else
894 tmp = None;
895 if (tmp) {
897 * We have to copy the shm Pixmap into a normal Pixmap because
898 * otherwise, we would have to control when Pixmaps are freed so
899 * that we can detach their shm segments. This is a problem if the
900 * program crash, leaving stale shared memory segments in the
901 * system (lots of them). But with some work, we can optimize
902 * things and remove this XCopyArea. This will require
903 * explicitly freeing all pixmaps when exiting or restarting
904 * wmaker.
906 XCopyArea(context->dpy, tmp, *pixmap, context->copy_gc, 0, 0, image->width, image->height, 0, 0);
907 XFreePixmap(context->dpy, tmp);
908 } else {
909 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
911 #else /* !USE_XSHM */
912 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
913 #endif /* !USE_XSHM */
915 RDestroyXImage(context, ximg);
917 return True;
920 /* make the gc permanent (create with context creation).
921 * GC creation is very expensive. altering its properties is not. -Dan
923 int RConvertImageMask(RContext * context, RImage * image, Pixmap * pixmap, Pixmap * mask, int threshold)
925 GC gc;
926 XGCValues gcv;
927 RXImage *ximg = NULL;
929 assert(context != NULL);
930 assert(image != NULL);
931 assert(pixmap != NULL);
932 assert(mask != NULL);
934 if (!RConvertImage(context, image, pixmap))
935 return False;
937 if (image->format == RRGBFormat) {
938 *mask = None;
939 return True;
942 ximg = image2Bitmap(context, image, threshold);
944 if (!ximg) {
945 return False;
947 *mask = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, 1);
948 gcv.foreground = context->black;
949 gcv.background = context->white;
950 gcv.graphics_exposures = False;
951 gc = XCreateGC(context->dpy, *mask, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
952 RPutXImage(context, *mask, gc, ximg, 0, 0, 0, 0, image->width, image->height);
953 RDestroyXImage(context, ximg);
954 XFreeGC(context->dpy, gc);
956 return True;
959 Bool RGetClosestXColor(RContext * context, const RColor * color, XColor * retColor)
961 if (context->vclass == TrueColor) {
962 unsigned short rmask, gmask, bmask;
963 unsigned short roffs, goffs, boffs;
964 unsigned short *rtable, *gtable, *btable;
966 roffs = context->red_offset;
967 goffs = context->green_offset;
968 boffs = context->blue_offset;
970 rmask = context->visual->red_mask >> roffs;
971 gmask = context->visual->green_mask >> goffs;
972 bmask = context->visual->blue_mask >> boffs;
974 rtable = computeTable(rmask);
975 gtable = computeTable(gmask);
976 btable = computeTable(bmask);
978 retColor->pixel = (((unsigned long) rtable[color->red]) << roffs)
979 | (((unsigned long) gtable[color->green]) << goffs)
980 | (((unsigned long) btable[color->blue]) << boffs);
982 retColor->red = color->red << 8;
983 retColor->green = color->green << 8;
984 retColor->blue = color->blue << 8;
985 retColor->flags = DoRed | DoGreen | DoBlue;
987 } else if (context->vclass == PseudoColor || context->vclass == StaticColor) {
989 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
990 unsigned int *rtable, *gtable, *btable;
992 rtable = computeStdTable(context->std_rgb_map->red_mult, context->std_rgb_map->red_max);
994 gtable = computeStdTable(context->std_rgb_map->green_mult,
995 context->std_rgb_map->green_max);
997 btable = computeStdTable(context->std_rgb_map->blue_mult, context->std_rgb_map->blue_max);
999 if (rtable == NULL || gtable == NULL || btable == NULL) {
1000 RErrorCode = RERR_NOMEMORY;
1001 return False;
1004 retColor->pixel = (rtable[color->red]
1005 + gtable[color->green]
1006 + btable[color->blue]
1007 + context->std_rgb_map->base_pixel) & 0xffffffff;
1008 retColor->red = color->red << 8;
1009 retColor->green = color->green << 8;
1010 retColor->blue = color->blue << 8;
1011 retColor->flags = DoRed | DoGreen | DoBlue;
1013 } else {
1014 const int cpc = context->attribs->colors_per_channel;
1015 const unsigned short rmask = cpc - 1; /* different sizes could be used */
1016 const unsigned short gmask = rmask; /* for r,g,b */
1017 const unsigned short bmask = rmask;
1018 unsigned short *rtable, *gtable, *btable;
1019 const int cpccpc = cpc * cpc;
1020 int index;
1022 rtable = computeTable(rmask);
1023 gtable = computeTable(gmask);
1024 btable = computeTable(bmask);
1026 if (rtable == NULL || gtable == NULL || btable == NULL) {
1027 RErrorCode = RERR_NOMEMORY;
1028 return False;
1030 index = rtable[color->red] * cpccpc + gtable[color->green] * cpc + btable[color->blue];
1031 *retColor = context->colors[index];
1034 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
1036 const int cpc = context->attribs->colors_per_channel;
1037 unsigned short gmask;
1038 unsigned short *table;
1039 int index;
1041 if (context->vclass == StaticGray)
1042 gmask = (1 << context->depth) - 1; /* use all grays */
1043 else
1044 gmask = cpc * cpc * cpc - 1;
1046 table = computeTable(gmask);
1047 if (!table)
1048 return False;
1050 index = table[(color->red * 30 + color->green * 59 + color->blue * 11) / 100];
1052 *retColor = context->colors[index];
1053 } else {
1054 RErrorCode = RERR_INTERNAL;
1055 return False;
1058 return True;