wrlib: return NULL if XImage could not be taken, for consistency
[wmaker-crm.git] / wrlib / convert.c
blobb0ab230312694ee02b435a1c2d4236bf767a2320
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"
40 #ifdef USE_XSHM
41 extern Pixmap R_CreateXImageMappedPixmap(RContext * context, RXImage * ximage);
42 #endif
44 #define NFREE(n) if (n) free(n)
46 #define HAS_ALPHA(I) ((I)->format == RRGBAFormat)
48 typedef struct RConversionTable {
49 unsigned short table[256];
50 unsigned short index;
52 struct RConversionTable *next;
53 } RConversionTable;
55 typedef struct RStdConversionTable {
56 unsigned int table[256];
58 unsigned short mult;
59 unsigned short max;
61 struct RStdConversionTable *next;
62 } RStdConversionTable;
64 static RConversionTable *conversionTable = NULL;
65 static RStdConversionTable *stdConversionTable = NULL;
67 static void release_conversion_table(void)
69 RConversionTable *tmp = conversionTable;
71 while (tmp) {
72 RConversionTable *tmp_to_delete = tmp;
74 tmp = tmp->next;
75 free(tmp_to_delete);
77 conversionTable = NULL;
80 static void release_std_conversion_table(void)
82 RStdConversionTable *tmp = stdConversionTable;
84 while (tmp) {
85 RStdConversionTable *tmp_to_delete = tmp;
87 tmp = tmp->next;
88 free(tmp_to_delete);
90 stdConversionTable = NULL;
93 void r_destroy_conversion_tables(void)
95 release_conversion_table();
96 release_std_conversion_table();
99 static unsigned short *computeTable(unsigned short mask)
101 RConversionTable *tmp = conversionTable;
102 int i;
104 while (tmp) {
105 if (tmp->index == mask)
106 break;
107 tmp = tmp->next;
110 if (tmp)
111 return tmp->table;
113 tmp = (RConversionTable *) malloc(sizeof(RConversionTable));
114 if (tmp == NULL)
115 return NULL;
117 for (i = 0; i < 256; i++)
118 tmp->table[i] = (i * mask + 0x7f) / 0xff;
120 tmp->index = mask;
121 tmp->next = conversionTable;
122 conversionTable = tmp;
123 return tmp->table;
126 static unsigned int *computeStdTable(unsigned int mult, unsigned int max)
128 RStdConversionTable *tmp = stdConversionTable;
129 unsigned int i;
131 while (tmp) {
132 if (tmp->mult == mult && tmp->max == max)
133 break;
134 tmp = tmp->next;
137 if (tmp)
138 return tmp->table;
140 tmp = (RStdConversionTable *) malloc(sizeof(RStdConversionTable));
141 if (tmp == NULL)
142 return NULL;
144 for (i = 0; i < 256; i++) {
145 tmp->table[i] = (i * max) / 0xff * mult;
147 tmp->mult = mult;
148 tmp->max = max;
150 tmp->next = stdConversionTable;
151 stdConversionTable = tmp;
153 return tmp->table;
156 /***************************************************************************/
158 static void
159 convertTrueColor_generic(RXImage * ximg, RImage * image,
160 signed char *err, signed char *nerr,
161 const unsigned short *rtable,
162 const unsigned short *gtable,
163 const unsigned short *btable,
164 const int dr, const int dg, const int db,
165 const unsigned short roffs, const unsigned short goffs, const unsigned short boffs)
167 signed char *terr;
168 int x, y, r, g, b;
169 int pixel;
170 int rer, ger, ber;
171 unsigned char *ptr = image->data;
172 int channels = (HAS_ALPHA(image) ? 4 : 3);
174 /* convert and dither the image to XImage */
175 for (y = 0; y < image->height; y++) {
176 nerr[0] = 0;
177 nerr[1] = 0;
178 nerr[2] = 0;
179 for (x = 0; x < image->width; x++, ptr += channels) {
181 /* reduce pixel */
182 pixel = *ptr + err[x];
183 if (pixel < 0)
184 pixel = 0;
185 else if (pixel > 0xff)
186 pixel = 0xff;
187 r = rtable[pixel];
188 /* calc error */
189 rer = pixel - r * dr;
191 /* reduce pixel */
192 pixel = *(ptr + 1) + err[x + 1];
193 if (pixel < 0)
194 pixel = 0;
195 else if (pixel > 0xff)
196 pixel = 0xff;
197 g = gtable[pixel];
198 /* calc error */
199 ger = pixel - g * dg;
201 /* reduce pixel */
202 pixel = *(ptr + 2) + err[x + 2];
203 if (pixel < 0)
204 pixel = 0;
205 else if (pixel > 0xff)
206 pixel = 0xff;
207 b = btable[pixel];
208 /* calc error */
209 ber = pixel - b * db;
211 pixel = (r << roffs) | (g << goffs) | (b << boffs);
212 XPutPixel(ximg->image, x, y, pixel);
214 /* distribute error */
215 r = (rer * 3) / 8;
216 g = (ger * 3) / 8;
217 b = (ber * 3) / 8;
218 /* x+1, y */
219 err[x + 3 * 1] += r;
220 err[x + 1 + 3 * 1] += g;
221 err[x + 2 + 3 * 1] += b;
222 /* x, y+1 */
223 nerr[x] += r;
224 nerr[x + 1] += g;
225 nerr[x + 2] += b;
226 /* x+1, y+1 */
227 nerr[x + 3 * 1] = rer - 2 * r;
228 nerr[x + 1 + 3 * 1] = ger - 2 * g;
229 nerr[x + 2 + 3 * 1] = ber - 2 * b;
231 /* skip to next line */
232 terr = err;
233 err = nerr;
234 nerr = terr;
237 /* redither the 1st line to distribute error better */
238 ptr = image->data;
239 y = 0;
240 nerr[0] = 0;
241 nerr[1] = 0;
242 nerr[2] = 0;
243 for (x = 0; x < image->width; x++, ptr += channels) {
245 /* reduce pixel */
246 pixel = *ptr + err[x];
247 if (pixel < 0)
248 pixel = 0;
249 else if (pixel > 0xff)
250 pixel = 0xff;
251 r = rtable[pixel];
252 /* calc error */
253 rer = pixel - r * dr;
255 /* reduce pixel */
256 pixel = *(ptr + 1) + err[x + 1];
257 if (pixel < 0)
258 pixel = 0;
259 else if (pixel > 0xff)
260 pixel = 0xff;
261 g = gtable[pixel];
262 /* calc error */
263 ger = pixel - g * dg;
265 /* reduce pixel */
266 pixel = *(ptr + 2) + err[x + 2];
267 if (pixel < 0)
268 pixel = 0;
269 else if (pixel > 0xff)
270 pixel = 0xff;
271 b = btable[pixel];
272 /* calc error */
273 ber = pixel - b * db;
275 pixel = (r << roffs) | (g << goffs) | (b << boffs);
276 XPutPixel(ximg->image, x, y, pixel);
278 /* distribute error */
279 r = (rer * 3) / 8;
280 g = (ger * 3) / 8;
281 b = (ber * 3) / 8;
282 /* x+1, y */
283 err[x + 3 * 1] += r;
284 err[x + 1 + 3 * 1] += g;
285 err[x + 2 + 3 * 1] += b;
286 /* x, y+1 */
287 nerr[x] += r;
288 nerr[x + 1] += g;
289 nerr[x + 2] += b;
290 /* x+1, y+1 */
291 nerr[x + 3 * 1] = rer - 2 * r;
292 nerr[x + 1 + 3 * 1] = ger - 2 * g;
293 nerr[x + 2 + 3 * 1] = ber - 2 * b;
297 static RXImage *image2TrueColor(RContext * ctx, RImage * image)
299 RXImage *ximg;
300 unsigned short rmask, gmask, bmask;
301 unsigned short roffs, goffs, boffs;
302 unsigned short *rtable, *gtable, *btable;
303 int channels = (HAS_ALPHA(image) ? 4 : 3);
305 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
306 if (!ximg) {
307 return NULL;
310 roffs = ctx->red_offset;
311 goffs = ctx->green_offset;
312 boffs = ctx->blue_offset;
314 rmask = ctx->visual->red_mask >> roffs;
315 gmask = ctx->visual->green_mask >> goffs;
316 bmask = ctx->visual->blue_mask >> boffs;
318 rtable = computeTable(rmask);
319 gtable = computeTable(gmask);
320 btable = computeTable(bmask);
322 if (rtable == NULL || gtable == NULL || btable == NULL) {
323 RErrorCode = RERR_NOMEMORY;
324 RDestroyXImage(ctx, ximg);
325 return NULL;
328 if (ctx->attribs->render_mode == RBestMatchRendering) {
329 int ofs;
330 unsigned long r, g, b;
331 int x, y;
332 unsigned long pixel;
333 unsigned char *ptr = image->data;
335 /* fake match */
336 #ifdef WRLIB_DEBUG
337 fputs("true color match\n", stderr);
338 #endif
339 if (rmask == 0xff && gmask == 0xff && bmask == 0xff) {
340 for (y = 0; y < image->height; y++) {
341 for (x = 0; x < image->width; x++, ptr += channels) {
342 /* reduce pixel */
343 r = ptr[0];
344 g = ptr[1];
345 b = ptr[2];
346 pixel = (r << roffs) | (g << goffs) | (b << boffs);
347 XPutPixel(ximg->image, x, y, pixel);
350 } else {
351 for (y = 0, ofs = 0; y < image->height; y++) {
352 for (x = 0; x < image->width; x++, ofs += channels - 3) {
353 /* reduce pixel */
354 r = rtable[ptr[ofs++]];
355 g = gtable[ptr[ofs++]];
356 b = btable[ptr[ofs++]];
357 pixel = (r << roffs) | (g << goffs) | (b << boffs);
358 XPutPixel(ximg->image, x, y, pixel);
362 } else {
363 /* dither */
364 const int dr = 0xff / rmask;
365 const int dg = 0xff / gmask;
366 const int db = 0xff / bmask;
368 #ifdef WRLIB_DEBUG
369 fputs("true color dither\n", stderr);
370 #endif
373 signed char *err;
374 signed char *nerr;
375 int ch = (HAS_ALPHA(image) ? 4 : 3);
377 err = malloc(ch * (image->width + 2));
378 nerr = malloc(ch * (image->width + 2));
379 if (!err || !nerr) {
380 NFREE(err);
381 NFREE(nerr);
382 RErrorCode = RERR_NOMEMORY;
383 RDestroyXImage(ctx, ximg);
384 return NULL;
387 memset(err, 0, ch * (image->width + 2));
388 memset(nerr, 0, ch * (image->width + 2));
390 convertTrueColor_generic(ximg, image, err, nerr,
391 rtable, gtable, btable, dr, dg, db, roffs, goffs, boffs);
392 free(err);
393 free(nerr);
398 return ximg;
401 /***************************************************************************/
403 static void
404 convertPseudoColor_to_8(RXImage * ximg, RImage * image,
405 signed char *err, signed char *nerr,
406 const unsigned short *rtable,
407 const unsigned short *gtable,
408 const unsigned short *btable,
409 const int dr, const int dg, const int db, unsigned long *pixels, int cpc)
411 signed char *terr;
412 int x, y, r, g, b;
413 int pixel;
414 int rer, ger, ber;
415 unsigned char *ptr = image->data;
416 unsigned char *optr = (unsigned char *)ximg->image->data;
417 int channels = (HAS_ALPHA(image) ? 4 : 3);
418 int cpcpc = cpc * cpc;
420 /* convert and dither the image to XImage */
421 for (y = 0; y < image->height; y++) {
422 nerr[0] = 0;
423 nerr[1] = 0;
424 nerr[2] = 0;
425 for (x = 0; x < image->width * 3; x += 3, ptr += channels) {
427 /* reduce pixel */
428 pixel = *ptr + err[x];
429 if (pixel < 0)
430 pixel = 0;
431 else if (pixel > 0xff)
432 pixel = 0xff;
433 r = rtable[pixel];
434 /* calc error */
435 rer = pixel - r * dr;
437 /* reduce pixel */
438 pixel = *(ptr + 1) + err[x + 1];
439 if (pixel < 0)
440 pixel = 0;
441 else if (pixel > 0xff)
442 pixel = 0xff;
443 g = gtable[pixel];
444 /* calc error */
445 ger = pixel - g * dg;
447 /* reduce pixel */
448 pixel = *(ptr + 2) + err[x + 2];
449 if (pixel < 0)
450 pixel = 0;
451 else if (pixel > 0xff)
452 pixel = 0xff;
453 b = btable[pixel];
454 /* calc error */
455 ber = pixel - b * db;
457 *optr++ = pixels[r * cpcpc + g * cpc + b];
459 /* distribute error */
460 r = (rer * 3) / 8;
461 g = (ger * 3) / 8;
462 b = (ber * 3) / 8;
464 /* x+1, y */
465 err[x + 3 * 1] += r;
466 err[x + 1 + 3 * 1] += g;
467 err[x + 2 + 3 * 1] += b;
468 /* x, y+1 */
469 nerr[x] += r;
470 nerr[x + 1] += g;
471 nerr[x + 2] += b;
472 /* x+1, y+1 */
473 nerr[x + 3 * 1] = rer - 2 * r;
474 nerr[x + 1 + 3 * 1] = ger - 2 * g;
475 nerr[x + 2 + 3 * 1] = ber - 2 * b;
477 /* skip to next line */
478 terr = err;
479 err = nerr;
480 nerr = terr;
482 optr += ximg->image->bytes_per_line - image->width;
486 static RXImage *image2PseudoColor(RContext * ctx, RImage * image)
488 RXImage *ximg;
489 register int x, y, r, g, b;
490 unsigned char *ptr;
491 unsigned long pixel;
492 const int cpc = ctx->attribs->colors_per_channel;
493 const unsigned short rmask = cpc - 1; /* different sizes could be used */
494 const unsigned short gmask = rmask; /* for r,g,b */
495 const unsigned short bmask = rmask;
496 unsigned short *rtable, *gtable, *btable;
497 const int cpccpc = cpc * cpc;
498 int channels = (HAS_ALPHA(image) ? 4 : 3);
500 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
501 if (!ximg) {
502 return NULL;
505 ptr = image->data;
507 /* Tables are same at the moment because rmask==gmask==bmask. */
508 rtable = computeTable(rmask);
509 gtable = computeTable(gmask);
510 btable = computeTable(bmask);
512 if (rtable == NULL || gtable == NULL || btable == NULL) {
513 RErrorCode = RERR_NOMEMORY;
514 RDestroyXImage(ctx, ximg);
515 return NULL;
518 if (ctx->attribs->render_mode == RBestMatchRendering) {
519 /* fake match */
520 #ifdef WRLIB_DEBUG
521 fprintf(stderr, "pseudo color match with %d colors per channel\n", cpc);
522 #endif
523 for (y = 0; y < image->height; y++) {
524 for (x = 0; x < image->width; x++, ptr += channels - 3) {
525 /* reduce pixel */
526 r = rtable[*ptr++];
527 g = gtable[*ptr++];
528 b = btable[*ptr++];
529 pixel = r * cpccpc + g * cpc + b;
530 /*data[ofs] = ctx->colors[pixel].pixel; */
531 XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel);
534 } else {
535 /* dither */
536 signed char *err;
537 signed char *nerr;
538 const int dr = 0xff / rmask;
539 const int dg = 0xff / gmask;
540 const int db = 0xff / bmask;
542 #ifdef WRLIB_DEBUG
543 fprintf(stderr, "pseudo color dithering with %d colors per channel\n", cpc);
544 #endif
545 err = malloc(4 * (image->width + 3));
546 nerr = malloc(4 * (image->width + 3));
547 if (!err || !nerr) {
548 NFREE(err);
549 NFREE(nerr);
550 RErrorCode = RERR_NOMEMORY;
551 RDestroyXImage(ctx, ximg);
552 return NULL;
554 memset(err, 0, 4 * (image->width + 3));
555 memset(nerr, 0, 4 * (image->width + 3));
557 convertPseudoColor_to_8(ximg, image, err + 4, nerr + 4,
558 rtable, gtable, btable, dr, dg, db, ctx->pixels, cpc);
560 free(err);
561 free(nerr);
564 return ximg;
568 * For standard colormap
570 static RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image)
572 RXImage *ximg;
573 register int x, y, r, g, b;
574 unsigned char *ptr;
575 unsigned long pixel;
576 unsigned char *data;
577 unsigned int *rtable, *gtable, *btable;
578 unsigned int base_pixel = ctx->std_rgb_map->base_pixel;
579 int channels = (HAS_ALPHA(image) ? 4 : 3);
581 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
582 if (!ximg) {
583 return NULL;
586 ptr = image->data;
588 data = (unsigned char *)ximg->image->data;
590 rtable = computeStdTable(ctx->std_rgb_map->red_mult, ctx->std_rgb_map->red_max);
592 gtable = computeStdTable(ctx->std_rgb_map->green_mult, ctx->std_rgb_map->green_max);
594 btable = computeStdTable(ctx->std_rgb_map->blue_mult, ctx->std_rgb_map->blue_max);
596 if (rtable == NULL || gtable == NULL || btable == NULL) {
597 RErrorCode = RERR_NOMEMORY;
598 RDestroyXImage(ctx, ximg);
599 return NULL;
602 if (ctx->attribs->render_mode == RBestMatchRendering) {
603 for (y = 0; y < image->height; y++) {
604 for (x = 0; x < image->width; x++, ptr += channels) {
605 /* reduce pixel */
606 pixel = (rtable[*ptr] + gtable[*(ptr + 1)]
607 + btable[*(ptr + 2)] + base_pixel) & 0xffffffff;
609 XPutPixel(ximg->image, x, y, pixel);
612 } else {
613 /* dither */
614 signed short *err, *nerr;
615 signed short *terr;
616 int rer, ger, ber;
617 int x1, ofs;
619 #ifdef WRLIB_DEBUG
620 fprintf(stderr, "pseudo color dithering with %d colors per channel\n",
621 ctx->attribs->colors_per_channel);
622 #endif
623 err = (short *)malloc(3 * (image->width + 2) * sizeof(short));
624 nerr = (short *)malloc(3 * (image->width + 2) * sizeof(short));
625 if (!err || !nerr) {
626 NFREE(err);
627 NFREE(nerr);
628 RErrorCode = RERR_NOMEMORY;
629 RDestroyXImage(ctx, ximg);
630 return NULL;
632 for (x = 0, x1 = 0; x < image->width * 3; x1 += channels - 3) {
633 err[x++] = ptr[x1++];
634 err[x++] = ptr[x1++];
635 err[x++] = ptr[x1++];
637 err[x] = err[x + 1] = err[x + 2] = 0;
638 /* convert and dither the image to XImage */
639 for (y = 0, ofs = 0; y < image->height; y++) {
640 if (y < image->height - 1) {
641 int x1;
642 for (x = 0, x1 = (y + 1) * image->width * channels;
643 x < image->width * 3; x1 += channels - 3) {
644 nerr[x++] = ptr[x1++];
645 nerr[x++] = ptr[x1++];
646 nerr[x++] = ptr[x1++];
648 /* last column */
649 x1 -= channels;
650 nerr[x++] = ptr[x1++];
651 nerr[x++] = ptr[x1++];
652 nerr[x++] = ptr[x1++];
654 for (x = 0; x < image->width * 3; x += 3, ofs++) {
655 /* reduce pixel */
656 if (err[x] > 0xff)
657 err[x] = 0xff;
658 else if (err[x] < 0)
659 err[x] = 0;
660 if (err[x + 1] > 0xff)
661 err[x + 1] = 0xff;
662 else if (err[x + 1] < 0)
663 err[x + 1] = 0;
664 if (err[x + 2] > 0xff)
665 err[x + 2] = 0xff;
666 else if (err[x + 2] < 0)
667 err[x + 2] = 0;
669 r = rtable[err[x]];
670 g = gtable[err[x + 1]];
671 b = btable[err[x + 2]];
673 pixel = r + g + b;
675 data[ofs] = base_pixel + pixel;
677 /* calc error */
678 rer = err[x] - (ctx->colors[pixel].red >> 8);
679 ger = err[x + 1] - (ctx->colors[pixel].green >> 8);
680 ber = err[x + 2] - (ctx->colors[pixel].blue >> 8);
682 /* distribute error */
683 err[x + 3 * 1] += (rer * 7) / 16;
684 err[x + 1 + 3 * 1] += (ger * 7) / 16;
685 err[x + 2 + 3 * 1] += (ber * 7) / 16;
687 nerr[x] += (rer * 5) / 16;
688 nerr[x + 1] += (ger * 5) / 16;
689 nerr[x + 2] += (ber * 5) / 16;
691 if (x > 0) {
692 nerr[x - 3 * 1] += (rer * 3) / 16;
693 nerr[x - 3 * 1 + 1] += (ger * 3) / 16;
694 nerr[x - 3 * 1 + 2] += (ber * 3) / 16;
697 nerr[x + 3 * 1] += rer / 16;
698 nerr[x + 1 + 3 * 1] += ger / 16;
699 nerr[x + 2 + 3 * 1] += ber / 16;
701 /* skip to next line */
702 terr = err;
703 err = nerr;
704 nerr = terr;
706 ofs += ximg->image->bytes_per_line - image->width;
708 free(err);
709 free(nerr);
711 ximg->image->data = (char *)data;
713 return ximg;
716 static RXImage *image2GrayScale(RContext * ctx, RImage * image)
718 RXImage *ximg;
719 register int x, y, g;
720 unsigned char *ptr;
721 const int cpc = ctx->attribs->colors_per_channel;
722 unsigned short gmask;
723 unsigned short *table;
724 unsigned char *data;
725 int channels = (HAS_ALPHA(image) ? 4 : 3);
727 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
728 if (!ximg) {
729 return NULL;
732 ptr = image->data;
734 data = (unsigned char *)ximg->image->data;
736 if (ctx->vclass == StaticGray)
737 gmask = (1 << ctx->depth) - 1; /* use all grays */
738 else
739 gmask = cpc * cpc * cpc - 1;
741 table = computeTable(gmask);
743 if (table == NULL) {
744 RErrorCode = RERR_NOMEMORY;
745 RDestroyXImage(ctx, ximg);
746 return NULL;
749 if (ctx->attribs->render_mode == RBestMatchRendering) {
750 /* fake match */
751 #ifdef WRLIB_DEBUG
752 fprintf(stderr, "grayscale match with %d colors per channel\n", cpc);
753 #endif
754 for (y = 0; y < image->height; y++) {
755 for (x = 0; x < image->width; x++) {
756 /* reduce pixel */
757 g = table[(*ptr * 30 + *(ptr + 1) * 59 + *(ptr + 2) * 11) / 100];
758 ptr += channels;
759 /*data[ofs] = ctx->colors[g].pixel; */
760 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
763 } else {
764 /* dither */
765 short *gerr;
766 short *ngerr;
767 short *terr;
768 int ger;
769 const int dg = 0xff / gmask;
771 #ifdef WRLIB_DEBUG
772 fprintf(stderr, "grayscale dither with %d colors per channel\n", cpc);
773 #endif
774 gerr = (short *)malloc((image->width + 2) * sizeof(short));
775 ngerr = (short *)malloc((image->width + 2) * sizeof(short));
776 if (!gerr || !ngerr) {
777 NFREE(gerr);
778 NFREE(ngerr);
779 RErrorCode = RERR_NOMEMORY;
780 RDestroyXImage(ctx, ximg);
781 return NULL;
783 for (x = 0, y = 0; x < image->width; x++, y += channels) {
784 gerr[x] = (ptr[y] * 30 + ptr[y + 1] * 59 + ptr[y + 2] * 11) / 100;
786 gerr[x] = 0;
787 /* convert and dither the image to XImage */
788 for (y = 0; y < image->height; y++) {
789 if (y < image->height - 1) {
790 int x1;
791 for (x = 0, x1 = (y + 1) * image->width * channels; x < image->width;
792 x++, x1 += channels) {
793 ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100;
795 /* last column */
796 x1 -= channels;
797 ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100;
799 for (x = 0; x < image->width; x++) {
800 /* reduce pixel */
801 if (gerr[x] > 0xff)
802 gerr[x] = 0xff;
803 else if (gerr[x] < 0)
804 gerr[x] = 0;
806 g = table[gerr[x]];
808 /*data[ofs] = ctx->colors[g].pixel; */
809 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
810 /* calc error */
811 ger = gerr[x] - g * dg;
813 /* distribute error */
814 g = (ger * 3) / 8;
815 /* x+1, y */
816 gerr[x + 1] += g;
817 /* x, y+1 */
818 ngerr[x] += g;
819 /* x+1, y+1 */
820 ngerr[x + 1] += ger - 2 * g;
822 /* skip to next line */
823 terr = gerr;
824 gerr = ngerr;
825 ngerr = terr;
827 free(gerr);
828 free(ngerr);
830 ximg->image->data = (char *)data;
832 return ximg;
835 static RXImage *image2Bitmap(RContext * ctx, RImage * image, int threshold)
837 RXImage *ximg;
838 unsigned char *alpha;
839 int x, y;
841 ximg = RCreateXImage(ctx, 1, image->width, image->height);
842 if (!ximg) {
843 return NULL;
845 alpha = image->data + 3;
847 for (y = 0; y < image->height; y++) {
848 for (x = 0; x < image->width; x++) {
849 XPutPixel(ximg->image, x, y, (*alpha <= threshold ? 0 : 1));
850 alpha += 4;
854 return ximg;
857 int RConvertImage(RContext * context, RImage * image, Pixmap * pixmap)
859 RXImage *ximg = NULL;
860 #ifdef USE_XSHM
861 Pixmap tmp;
862 #endif
864 assert(context != NULL);
865 assert(image != NULL);
866 assert(pixmap != NULL);
868 switch (context->vclass) {
869 case TrueColor:
870 ximg = image2TrueColor(context, image);
871 break;
873 case PseudoColor:
874 case StaticColor:
875 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap)
876 ximg = image2StandardPseudoColor(context, image);
877 else
878 ximg = image2PseudoColor(context, image);
879 break;
881 case GrayScale:
882 case StaticGray:
883 ximg = image2GrayScale(context, image);
884 break;
887 if (!ximg) {
888 return False;
891 *pixmap = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, context->depth);
893 #ifdef USE_XSHM
894 if (context->flags.use_shared_pixmap && ximg->is_shared)
895 tmp = R_CreateXImageMappedPixmap(context, ximg);
896 else
897 tmp = None;
898 if (tmp) {
900 * We have to copy the shm Pixmap into a normal Pixmap because
901 * otherwise, we would have to control when Pixmaps are freed so
902 * that we can detach their shm segments. This is a problem if the
903 * program crash, leaving stale shared memory segments in the
904 * system (lots of them). But with some work, we can optimize
905 * things and remove this XCopyArea. This will require
906 * explicitly freeing all pixmaps when exiting or restarting
907 * wmaker.
909 XCopyArea(context->dpy, tmp, *pixmap, context->copy_gc, 0, 0, image->width, image->height, 0, 0);
910 XFreePixmap(context->dpy, tmp);
911 } else {
912 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
914 #else /* !USE_XSHM */
915 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
916 #endif /* !USE_XSHM */
918 RDestroyXImage(context, ximg);
920 return True;
923 /* make the gc permanent (create with context creation).
924 * GC creation is very expensive. altering its properties is not. -Dan
926 int RConvertImageMask(RContext * context, RImage * image, Pixmap * pixmap, Pixmap * mask, int threshold)
928 GC gc;
929 XGCValues gcv;
930 RXImage *ximg = NULL;
932 assert(context != NULL);
933 assert(image != NULL);
934 assert(pixmap != NULL);
935 assert(mask != NULL);
937 if (!RConvertImage(context, image, pixmap))
938 return False;
940 if (image->format == RRGBFormat) {
941 *mask = None;
942 return True;
945 ximg = image2Bitmap(context, image, threshold);
947 if (!ximg) {
948 return False;
950 *mask = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, 1);
951 gcv.foreground = context->black;
952 gcv.background = context->white;
953 gcv.graphics_exposures = False;
954 gc = XCreateGC(context->dpy, *mask, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
955 RPutXImage(context, *mask, gc, ximg, 0, 0, 0, 0, image->width, image->height);
956 RDestroyXImage(context, ximg);
957 XFreeGC(context->dpy, gc);
959 return True;
962 Bool RGetClosestXColor(RContext * context, const RColor * color, XColor * retColor)
964 if (context->vclass == TrueColor) {
965 unsigned short rmask, gmask, bmask;
966 unsigned short roffs, goffs, boffs;
967 unsigned short *rtable, *gtable, *btable;
969 roffs = context->red_offset;
970 goffs = context->green_offset;
971 boffs = context->blue_offset;
973 rmask = context->visual->red_mask >> roffs;
974 gmask = context->visual->green_mask >> goffs;
975 bmask = context->visual->blue_mask >> boffs;
977 rtable = computeTable(rmask);
978 gtable = computeTable(gmask);
979 btable = computeTable(bmask);
981 retColor->pixel = (((unsigned long) rtable[color->red]) << roffs)
982 | (((unsigned long) gtable[color->green]) << goffs)
983 | (((unsigned long) btable[color->blue]) << boffs);
985 retColor->red = color->red << 8;
986 retColor->green = color->green << 8;
987 retColor->blue = color->blue << 8;
988 retColor->flags = DoRed | DoGreen | DoBlue;
990 } else if (context->vclass == PseudoColor || context->vclass == StaticColor) {
992 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
993 unsigned int *rtable, *gtable, *btable;
995 rtable = computeStdTable(context->std_rgb_map->red_mult, context->std_rgb_map->red_max);
997 gtable = computeStdTable(context->std_rgb_map->green_mult,
998 context->std_rgb_map->green_max);
1000 btable = computeStdTable(context->std_rgb_map->blue_mult, context->std_rgb_map->blue_max);
1002 if (rtable == NULL || gtable == NULL || btable == NULL) {
1003 RErrorCode = RERR_NOMEMORY;
1004 return False;
1007 retColor->pixel = (rtable[color->red]
1008 + gtable[color->green]
1009 + btable[color->blue]
1010 + context->std_rgb_map->base_pixel) & 0xffffffff;
1011 retColor->red = color->red << 8;
1012 retColor->green = color->green << 8;
1013 retColor->blue = color->blue << 8;
1014 retColor->flags = DoRed | DoGreen | DoBlue;
1016 } else {
1017 const int cpc = context->attribs->colors_per_channel;
1018 const unsigned short rmask = cpc - 1; /* different sizes could be used */
1019 const unsigned short gmask = rmask; /* for r,g,b */
1020 const unsigned short bmask = rmask;
1021 unsigned short *rtable, *gtable, *btable;
1022 const int cpccpc = cpc * cpc;
1023 int index;
1025 rtable = computeTable(rmask);
1026 gtable = computeTable(gmask);
1027 btable = computeTable(bmask);
1029 if (rtable == NULL || gtable == NULL || btable == NULL) {
1030 RErrorCode = RERR_NOMEMORY;
1031 return False;
1033 index = rtable[color->red] * cpccpc + gtable[color->green] * cpc + btable[color->blue];
1034 *retColor = context->colors[index];
1037 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
1039 const int cpc = context->attribs->colors_per_channel;
1040 unsigned short gmask;
1041 unsigned short *table;
1042 int index;
1044 if (context->vclass == StaticGray)
1045 gmask = (1 << context->depth) - 1; /* use all grays */
1046 else
1047 gmask = cpc * cpc * cpc - 1;
1049 table = computeTable(gmask);
1050 if (!table)
1051 return False;
1053 index = table[(color->red * 30 + color->green * 59 + color->blue * 11) / 100];
1055 *retColor = context->colors[index];
1056 } else {
1057 RErrorCode = RERR_INTERNAL;
1058 return False;
1061 return True;