Update Serbian translation from master branch
[wmaker-crm.git] / wrlib / convert.c
blob400234f07e6a57a26b5df83019d4b9cd37b8ea99
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"
39 #include "wr_i18n.h"
42 #define NFREE(n) if (n) free(n)
44 #define HAS_ALPHA(I) ((I)->format == RRGBAFormat)
46 typedef struct RConversionTable {
47 unsigned short table[256];
48 unsigned short index;
50 struct RConversionTable *next;
51 } RConversionTable;
53 typedef struct RStdConversionTable {
54 unsigned int table[256];
56 unsigned short mult;
57 unsigned short max;
59 struct RStdConversionTable *next;
60 } RStdConversionTable;
62 static RConversionTable *conversionTable = NULL;
63 static RStdConversionTable *stdConversionTable = NULL;
65 static void release_conversion_table(void)
67 RConversionTable *tmp = conversionTable;
69 while (tmp) {
70 RConversionTable *tmp_to_delete = tmp;
72 tmp = tmp->next;
73 free(tmp_to_delete);
75 conversionTable = NULL;
78 static void release_std_conversion_table(void)
80 RStdConversionTable *tmp = stdConversionTable;
82 while (tmp) {
83 RStdConversionTable *tmp_to_delete = tmp;
85 tmp = tmp->next;
86 free(tmp_to_delete);
88 stdConversionTable = NULL;
91 void r_destroy_conversion_tables(void)
93 release_conversion_table();
94 release_std_conversion_table();
97 static unsigned short *computeTable(unsigned short mask)
99 RConversionTable *tmp = conversionTable;
100 int i;
102 while (tmp) {
103 if (tmp->index == mask)
104 break;
105 tmp = tmp->next;
108 if (tmp)
109 return tmp->table;
111 tmp = (RConversionTable *) malloc(sizeof(RConversionTable));
112 if (tmp == NULL)
113 return NULL;
115 for (i = 0; i < 256; i++)
116 tmp->table[i] = (i * mask + 0x7f) / 0xff;
118 tmp->index = mask;
119 tmp->next = conversionTable;
120 conversionTable = tmp;
121 return tmp->table;
124 static unsigned int *computeStdTable(unsigned int mult, unsigned int max)
126 RStdConversionTable *tmp = stdConversionTable;
127 unsigned int i;
129 while (tmp) {
130 if (tmp->mult == mult && tmp->max == max)
131 break;
132 tmp = tmp->next;
135 if (tmp)
136 return tmp->table;
138 tmp = (RStdConversionTable *) malloc(sizeof(RStdConversionTable));
139 if (tmp == NULL)
140 return NULL;
142 for (i = 0; i < 256; i++) {
143 tmp->table[i] = (i * max) / 0xff * mult;
145 tmp->mult = mult;
146 tmp->max = max;
148 tmp->next = stdConversionTable;
149 stdConversionTable = tmp;
151 return tmp->table;
154 /***************************************************************************/
156 static void
157 convertTrueColor_generic(RXImage * ximg, RImage * image,
158 signed char *err, signed char *nerr,
159 const unsigned short *rtable,
160 const unsigned short *gtable,
161 const unsigned short *btable,
162 const int dr, const int dg, const int db,
163 const unsigned short roffs, const unsigned short goffs, const unsigned short boffs)
165 signed char *terr;
166 int x, y, r, g, b;
167 int pixel;
168 int rer, ger, ber;
169 unsigned char *ptr = image->data;
170 int channels = (HAS_ALPHA(image) ? 4 : 3);
172 /* convert and dither the image to XImage */
173 for (y = 0; y < image->height; y++) {
174 nerr[0] = 0;
175 nerr[1] = 0;
176 nerr[2] = 0;
177 for (x = 0; x < image->width; x++, ptr += channels) {
179 /* reduce pixel */
180 pixel = *ptr + err[x];
181 if (pixel < 0)
182 pixel = 0;
183 else if (pixel > 0xff)
184 pixel = 0xff;
185 r = rtable[pixel];
186 /* calc error */
187 rer = pixel - r * dr;
189 /* reduce pixel */
190 pixel = *(ptr + 1) + err[x + 1];
191 if (pixel < 0)
192 pixel = 0;
193 else if (pixel > 0xff)
194 pixel = 0xff;
195 g = gtable[pixel];
196 /* calc error */
197 ger = pixel - g * dg;
199 /* reduce pixel */
200 pixel = *(ptr + 2) + err[x + 2];
201 if (pixel < 0)
202 pixel = 0;
203 else if (pixel > 0xff)
204 pixel = 0xff;
205 b = btable[pixel];
206 /* calc error */
207 ber = pixel - b * db;
209 pixel = (r << roffs) | (g << goffs) | (b << boffs);
210 XPutPixel(ximg->image, x, y, pixel);
212 /* distribute error */
213 r = (rer * 3) / 8;
214 g = (ger * 3) / 8;
215 b = (ber * 3) / 8;
216 /* x+1, y */
217 err[x + 3 * 1] += r;
218 err[x + 1 + 3 * 1] += g;
219 err[x + 2 + 3 * 1] += b;
220 /* x, y+1 */
221 nerr[x] += r;
222 nerr[x + 1] += g;
223 nerr[x + 2] += b;
224 /* x+1, y+1 */
225 nerr[x + 3 * 1] = rer - 2 * r;
226 nerr[x + 1 + 3 * 1] = ger - 2 * g;
227 nerr[x + 2 + 3 * 1] = ber - 2 * b;
229 /* skip to next line */
230 terr = err;
231 err = nerr;
232 nerr = terr;
235 /* redither the 1st line to distribute error better */
236 ptr = image->data;
237 y = 0;
238 nerr[0] = 0;
239 nerr[1] = 0;
240 nerr[2] = 0;
241 for (x = 0; x < image->width; x++, ptr += channels) {
243 /* reduce pixel */
244 pixel = *ptr + err[x];
245 if (pixel < 0)
246 pixel = 0;
247 else if (pixel > 0xff)
248 pixel = 0xff;
249 r = rtable[pixel];
250 /* calc error */
251 rer = pixel - r * dr;
253 /* reduce pixel */
254 pixel = *(ptr + 1) + err[x + 1];
255 if (pixel < 0)
256 pixel = 0;
257 else if (pixel > 0xff)
258 pixel = 0xff;
259 g = gtable[pixel];
260 /* calc error */
261 ger = pixel - g * dg;
263 /* reduce pixel */
264 pixel = *(ptr + 2) + err[x + 2];
265 if (pixel < 0)
266 pixel = 0;
267 else if (pixel > 0xff)
268 pixel = 0xff;
269 b = btable[pixel];
270 /* calc error */
271 ber = pixel - b * db;
273 pixel = (r << roffs) | (g << goffs) | (b << boffs);
274 XPutPixel(ximg->image, x, y, pixel);
276 /* distribute error */
277 r = (rer * 3) / 8;
278 g = (ger * 3) / 8;
279 b = (ber * 3) / 8;
280 /* x+1, y */
281 err[x + 3 * 1] += r;
282 err[x + 1 + 3 * 1] += g;
283 err[x + 2 + 3 * 1] += b;
284 /* x, y+1 */
285 nerr[x] += r;
286 nerr[x + 1] += g;
287 nerr[x + 2] += b;
288 /* x+1, y+1 */
289 nerr[x + 3 * 1] = rer - 2 * r;
290 nerr[x + 1 + 3 * 1] = ger - 2 * g;
291 nerr[x + 2 + 3 * 1] = ber - 2 * b;
295 static RXImage *image2TrueColor(RContext * ctx, RImage * image)
297 RXImage *ximg;
298 unsigned short rmask, gmask, bmask;
299 unsigned short roffs, goffs, boffs;
300 unsigned short *rtable, *gtable, *btable;
301 int channels = (HAS_ALPHA(image) ? 4 : 3);
303 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
304 if (!ximg) {
305 return NULL;
308 roffs = ctx->red_offset;
309 goffs = ctx->green_offset;
310 boffs = ctx->blue_offset;
312 rmask = ctx->visual->red_mask >> roffs;
313 gmask = ctx->visual->green_mask >> goffs;
314 bmask = ctx->visual->blue_mask >> boffs;
316 rtable = computeTable(rmask);
317 gtable = computeTable(gmask);
318 btable = computeTable(bmask);
320 if (rtable == NULL || gtable == NULL || btable == NULL) {
321 RErrorCode = RERR_NOMEMORY;
322 RDestroyXImage(ctx, ximg);
323 return NULL;
326 if (ctx->attribs->render_mode == RBestMatchRendering) {
327 int ofs;
328 unsigned long r, g, b;
329 int x, y;
330 unsigned long pixel;
331 unsigned char *ptr = image->data;
333 /* fake match */
334 #ifdef WRLIB_DEBUG
335 fputs("true color match\n", stderr);
336 #endif
337 if (rmask == 0xff && gmask == 0xff && bmask == 0xff) {
338 for (y = 0; y < image->height; y++) {
339 for (x = 0; x < image->width; x++, ptr += channels) {
340 /* reduce pixel */
341 r = ptr[0];
342 g = ptr[1];
343 b = ptr[2];
344 pixel = (r << roffs) | (g << goffs) | (b << boffs);
345 XPutPixel(ximg->image, x, y, pixel);
348 } else {
349 for (y = 0, ofs = 0; y < image->height; y++) {
350 for (x = 0; x < image->width; x++, ofs += channels - 3) {
351 /* reduce pixel */
352 r = rtable[ptr[ofs++]];
353 g = gtable[ptr[ofs++]];
354 b = btable[ptr[ofs++]];
355 pixel = (r << roffs) | (g << goffs) | (b << boffs);
356 XPutPixel(ximg->image, x, y, pixel);
360 } else {
361 /* dither */
362 const int dr = 0xff / rmask;
363 const int dg = 0xff / gmask;
364 const int db = 0xff / bmask;
366 #ifdef WRLIB_DEBUG
367 fputs("true color dither\n", stderr);
368 #endif
371 signed char *err;
372 signed char *nerr;
373 int ch = (HAS_ALPHA(image) ? 4 : 3);
375 err = malloc(ch * (image->width + 2));
376 nerr = malloc(ch * (image->width + 2));
377 if (!err || !nerr) {
378 NFREE(err);
379 NFREE(nerr);
380 RErrorCode = RERR_NOMEMORY;
381 RDestroyXImage(ctx, ximg);
382 return NULL;
385 memset(err, 0, ch * (image->width + 2));
386 memset(nerr, 0, ch * (image->width + 2));
388 convertTrueColor_generic(ximg, image, err, nerr,
389 rtable, gtable, btable, dr, dg, db, roffs, goffs, boffs);
390 free(err);
391 free(nerr);
396 return ximg;
399 /***************************************************************************/
401 static void
402 convertPseudoColor_to_8(RXImage * ximg, RImage * image,
403 signed char *err, signed char *nerr,
404 const unsigned short *rtable,
405 const unsigned short *gtable,
406 const unsigned short *btable,
407 const int dr, const int dg, const int db, unsigned long *pixels, int cpc)
409 signed char *terr;
410 int x, y, r, g, b;
411 int pixel;
412 int rer, ger, ber;
413 unsigned char *ptr = image->data;
414 unsigned char *optr = (unsigned char *)ximg->image->data;
415 int channels = (HAS_ALPHA(image) ? 4 : 3);
416 int cpcpc = cpc * cpc;
418 /* convert and dither the image to XImage */
419 for (y = 0; y < image->height; y++) {
420 nerr[0] = 0;
421 nerr[1] = 0;
422 nerr[2] = 0;
423 for (x = 0; x < image->width * 3; x += 3, ptr += channels) {
425 /* reduce pixel */
426 pixel = *ptr + err[x];
427 if (pixel < 0)
428 pixel = 0;
429 else if (pixel > 0xff)
430 pixel = 0xff;
431 r = rtable[pixel];
432 /* calc error */
433 rer = pixel - r * dr;
435 /* reduce pixel */
436 pixel = *(ptr + 1) + err[x + 1];
437 if (pixel < 0)
438 pixel = 0;
439 else if (pixel > 0xff)
440 pixel = 0xff;
441 g = gtable[pixel];
442 /* calc error */
443 ger = pixel - g * dg;
445 /* reduce pixel */
446 pixel = *(ptr + 2) + err[x + 2];
447 if (pixel < 0)
448 pixel = 0;
449 else if (pixel > 0xff)
450 pixel = 0xff;
451 b = btable[pixel];
452 /* calc error */
453 ber = pixel - b * db;
455 *optr++ = pixels[r * cpcpc + g * cpc + b];
457 /* distribute error */
458 r = (rer * 3) / 8;
459 g = (ger * 3) / 8;
460 b = (ber * 3) / 8;
462 /* x+1, y */
463 err[x + 3 * 1] += r;
464 err[x + 1 + 3 * 1] += g;
465 err[x + 2 + 3 * 1] += b;
466 /* x, y+1 */
467 nerr[x] += r;
468 nerr[x + 1] += g;
469 nerr[x + 2] += b;
470 /* x+1, y+1 */
471 nerr[x + 3 * 1] = rer - 2 * r;
472 nerr[x + 1 + 3 * 1] = ger - 2 * g;
473 nerr[x + 2 + 3 * 1] = ber - 2 * b;
475 /* skip to next line */
476 terr = err;
477 err = nerr;
478 nerr = terr;
480 optr += ximg->image->bytes_per_line - image->width;
484 static RXImage *image2PseudoColor(RContext * ctx, RImage * image)
486 RXImage *ximg;
487 register int x, y, r, g, b;
488 unsigned char *ptr;
489 unsigned long pixel;
490 const int cpc = ctx->attribs->colors_per_channel;
491 const unsigned short rmask = cpc - 1; /* different sizes could be used */
492 const unsigned short gmask = rmask; /* for r,g,b */
493 const unsigned short bmask = rmask;
494 unsigned short *rtable, *gtable, *btable;
495 const int cpccpc = cpc * cpc;
496 int channels = (HAS_ALPHA(image) ? 4 : 3);
498 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
499 if (!ximg) {
500 return NULL;
503 ptr = image->data;
505 /* Tables are same at the moment because rmask==gmask==bmask. */
506 rtable = computeTable(rmask);
507 gtable = computeTable(gmask);
508 btable = computeTable(bmask);
510 if (rtable == NULL || gtable == NULL || btable == NULL) {
511 RErrorCode = RERR_NOMEMORY;
512 RDestroyXImage(ctx, ximg);
513 return NULL;
516 if (ctx->attribs->render_mode == RBestMatchRendering) {
517 /* fake match */
518 #ifdef WRLIB_DEBUG
519 fprintf(stderr, "pseudo color match with %d colors per channel\n", cpc);
520 #endif
521 for (y = 0; y < image->height; y++) {
522 for (x = 0; x < image->width; x++, ptr += channels - 3) {
523 /* reduce pixel */
524 r = rtable[*ptr++];
525 g = gtable[*ptr++];
526 b = btable[*ptr++];
527 pixel = r * cpccpc + g * cpc + b;
528 /*data[ofs] = ctx->colors[pixel].pixel; */
529 XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel);
532 } else {
533 /* dither */
534 signed char *err;
535 signed char *nerr;
536 const int dr = 0xff / rmask;
537 const int dg = 0xff / gmask;
538 const int db = 0xff / bmask;
540 #ifdef WRLIB_DEBUG
541 fprintf(stderr, "pseudo color dithering with %d colors per channel\n", cpc);
542 #endif
543 err = malloc(4 * (image->width + 3));
544 nerr = malloc(4 * (image->width + 3));
545 if (!err || !nerr) {
546 NFREE(err);
547 NFREE(nerr);
548 RErrorCode = RERR_NOMEMORY;
549 RDestroyXImage(ctx, ximg);
550 return NULL;
552 memset(err, 0, 4 * (image->width + 3));
553 memset(nerr, 0, 4 * (image->width + 3));
555 convertPseudoColor_to_8(ximg, image, err + 4, nerr + 4,
556 rtable, gtable, btable, dr, dg, db, ctx->pixels, cpc);
558 free(err);
559 free(nerr);
562 return ximg;
566 * For standard colormap
568 static RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image)
570 RXImage *ximg;
571 register int x, y, r, g, b;
572 unsigned char *ptr;
573 unsigned long pixel;
574 unsigned char *data;
575 unsigned int *rtable, *gtable, *btable;
576 unsigned int base_pixel = ctx->std_rgb_map->base_pixel;
577 int channels = (HAS_ALPHA(image) ? 4 : 3);
579 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
580 if (!ximg) {
581 return NULL;
584 ptr = image->data;
586 data = (unsigned char *)ximg->image->data;
588 rtable = computeStdTable(ctx->std_rgb_map->red_mult, ctx->std_rgb_map->red_max);
590 gtable = computeStdTable(ctx->std_rgb_map->green_mult, ctx->std_rgb_map->green_max);
592 btable = computeStdTable(ctx->std_rgb_map->blue_mult, ctx->std_rgb_map->blue_max);
594 if (rtable == NULL || gtable == NULL || btable == NULL) {
595 RErrorCode = RERR_NOMEMORY;
596 RDestroyXImage(ctx, ximg);
597 return NULL;
600 if (ctx->attribs->render_mode == RBestMatchRendering) {
601 for (y = 0; y < image->height; y++) {
602 for (x = 0; x < image->width; x++, ptr += channels) {
603 /* reduce pixel */
604 pixel = (rtable[*ptr] + gtable[*(ptr + 1)]
605 + btable[*(ptr + 2)] + base_pixel) & 0xffffffff;
607 XPutPixel(ximg->image, x, y, pixel);
610 } else {
611 /* dither */
612 signed short *err, *nerr;
613 signed short *terr;
614 int rer, ger, ber;
615 int x1, ofs;
617 #ifdef WRLIB_DEBUG
618 fprintf(stderr, "pseudo color dithering with %d colors per channel\n",
619 ctx->attribs->colors_per_channel);
620 #endif
621 err = (short *)malloc(3 * (image->width + 2) * sizeof(short));
622 nerr = (short *)malloc(3 * (image->width + 2) * sizeof(short));
623 if (!err || !nerr) {
624 NFREE(err);
625 NFREE(nerr);
626 RErrorCode = RERR_NOMEMORY;
627 RDestroyXImage(ctx, ximg);
628 return NULL;
631 memset(err, 0, 3 * (image->width + 2) * sizeof(short));
632 memset(nerr, 0, 3 * (image->width + 2) * sizeof(short));
634 for (x = 0, x1 = 0; x < image->width * 3; x1 += channels - 3) {
635 err[x++] = ptr[x1++];
636 err[x++] = ptr[x1++];
637 err[x++] = ptr[x1++];
639 err[x] = err[x + 1] = err[x + 2] = 0;
640 /* convert and dither the image to XImage */
641 for (y = 0, ofs = 0; y < image->height; y++) {
642 if (y < image->height - 1) {
643 int x1;
644 for (x = 0, x1 = (y + 1) * image->width * channels;
645 x < image->width * 3; x1 += channels - 3) {
646 nerr[x++] = ptr[x1++];
647 nerr[x++] = ptr[x1++];
648 nerr[x++] = ptr[x1++];
650 /* last column */
651 x1 -= channels;
652 nerr[x++] = ptr[x1++];
653 nerr[x++] = ptr[x1++];
654 nerr[x++] = ptr[x1++];
656 for (x = 0; x < image->width * 3; x += 3, ofs++) {
657 /* reduce pixel */
658 if (err[x] > 0xff)
659 err[x] = 0xff;
660 else if (err[x] < 0)
661 err[x] = 0;
662 if (err[x + 1] > 0xff)
663 err[x + 1] = 0xff;
664 else if (err[x + 1] < 0)
665 err[x + 1] = 0;
666 if (err[x + 2] > 0xff)
667 err[x + 2] = 0xff;
668 else if (err[x + 2] < 0)
669 err[x + 2] = 0;
671 r = rtable[err[x]];
672 g = gtable[err[x + 1]];
673 b = btable[err[x + 2]];
675 pixel = r + g + b;
677 data[ofs] = base_pixel + pixel;
679 /* calc error */
680 rer = err[x] - (ctx->colors[pixel].red >> 8);
681 ger = err[x + 1] - (ctx->colors[pixel].green >> 8);
682 ber = err[x + 2] - (ctx->colors[pixel].blue >> 8);
684 /* distribute error */
685 err[x + 3 * 1] += (rer * 7) / 16;
686 err[x + 1 + 3 * 1] += (ger * 7) / 16;
687 err[x + 2 + 3 * 1] += (ber * 7) / 16;
689 nerr[x] += (rer * 5) / 16;
690 nerr[x + 1] += (ger * 5) / 16;
691 nerr[x + 2] += (ber * 5) / 16;
693 if (x > 0) {
694 nerr[x - 3 * 1] += (rer * 3) / 16;
695 nerr[x - 3 * 1 + 1] += (ger * 3) / 16;
696 nerr[x - 3 * 1 + 2] += (ber * 3) / 16;
699 nerr[x + 3 * 1] += rer / 16;
700 nerr[x + 1 + 3 * 1] += ger / 16;
701 nerr[x + 2 + 3 * 1] += ber / 16;
703 /* skip to next line */
704 terr = err;
705 err = nerr;
706 nerr = terr;
708 ofs += ximg->image->bytes_per_line - image->width;
710 free(err);
711 free(nerr);
713 ximg->image->data = (char *)data;
715 return ximg;
718 static RXImage *image2GrayScale(RContext * ctx, RImage * image)
720 RXImage *ximg;
721 register int x, y, g;
722 unsigned char *ptr;
723 const int cpc = ctx->attribs->colors_per_channel;
724 unsigned short gmask;
725 unsigned short *table;
726 unsigned char *data;
727 int channels = (HAS_ALPHA(image) ? 4 : 3);
729 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
730 if (!ximg) {
731 return NULL;
734 ptr = image->data;
736 data = (unsigned char *)ximg->image->data;
738 if (ctx->vclass == StaticGray)
739 gmask = (1 << ctx->depth) - 1; /* use all grays */
740 else
741 gmask = cpc * cpc * cpc - 1;
743 table = computeTable(gmask);
745 if (table == NULL) {
746 RErrorCode = RERR_NOMEMORY;
747 RDestroyXImage(ctx, ximg);
748 return NULL;
751 if (ctx->attribs->render_mode == RBestMatchRendering) {
752 /* fake match */
753 #ifdef WRLIB_DEBUG
754 fprintf(stderr, "grayscale match with %d colors per channel\n", cpc);
755 #endif
756 for (y = 0; y < image->height; y++) {
757 for (x = 0; x < image->width; x++) {
758 /* reduce pixel */
759 g = table[(*ptr * 30 + *(ptr + 1) * 59 + *(ptr + 2) * 11) / 100];
760 ptr += channels;
761 /*data[ofs] = ctx->colors[g].pixel; */
762 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
765 } else {
766 /* dither */
767 short *gerr;
768 short *ngerr;
769 short *terr;
770 int ger;
771 const int dg = 0xff / gmask;
773 #ifdef WRLIB_DEBUG
774 fprintf(stderr, "grayscale dither with %d colors per channel\n", cpc);
775 #endif
776 gerr = (short *)malloc((image->width + 2) * sizeof(short));
777 ngerr = (short *)malloc((image->width + 2) * sizeof(short));
778 if (!gerr || !ngerr) {
779 NFREE(gerr);
780 NFREE(ngerr);
781 RErrorCode = RERR_NOMEMORY;
782 RDestroyXImage(ctx, ximg);
783 return NULL;
786 memset(gerr, 0, (image->width + 2) * sizeof(short));
787 memset(ngerr, 0, (image->width + 2) * sizeof(short));
789 for (x = 0, y = 0; x < image->width; x++, y += channels) {
790 gerr[x] = (ptr[y] * 30 + ptr[y + 1] * 59 + ptr[y + 2] * 11) / 100;
792 gerr[x] = 0;
793 /* convert and dither the image to XImage */
794 for (y = 0; y < image->height; y++) {
795 if (y < image->height - 1) {
796 int x1;
797 for (x = 0, x1 = (y + 1) * image->width * channels; x < image->width;
798 x++, x1 += channels) {
799 ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100;
801 /* last column */
802 x1 -= channels;
803 ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100;
805 for (x = 0; x < image->width; x++) {
806 /* reduce pixel */
807 if (gerr[x] > 0xff)
808 gerr[x] = 0xff;
809 else if (gerr[x] < 0)
810 gerr[x] = 0;
812 g = table[gerr[x]];
814 /*data[ofs] = ctx->colors[g].pixel; */
815 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
816 /* calc error */
817 ger = gerr[x] - g * dg;
819 /* distribute error */
820 g = (ger * 3) / 8;
821 /* x+1, y */
822 gerr[x + 1] += g;
823 /* x, y+1 */
824 ngerr[x] += g;
825 /* x+1, y+1 */
826 ngerr[x + 1] += ger - 2 * g;
828 /* skip to next line */
829 terr = gerr;
830 gerr = ngerr;
831 ngerr = terr;
833 free(gerr);
834 free(ngerr);
836 ximg->image->data = (char *)data;
838 return ximg;
841 static RXImage *image2Bitmap(RContext * ctx, RImage * image, int threshold)
843 RXImage *ximg;
844 unsigned char *alpha;
845 int x, y;
847 ximg = RCreateXImage(ctx, 1, image->width, image->height);
848 if (!ximg) {
849 return NULL;
851 alpha = image->data + 3;
853 for (y = 0; y < image->height; y++) {
854 for (x = 0; x < image->width; x++) {
855 XPutPixel(ximg->image, x, y, (*alpha <= threshold ? 0 : 1));
856 alpha += 4;
860 return ximg;
863 int RConvertImage(RContext * context, RImage * image, Pixmap * pixmap)
865 RXImage *ximg = NULL;
866 #ifdef USE_XSHM
867 Pixmap tmp;
868 #endif
870 assert(context != NULL);
871 assert(image != NULL);
872 assert(pixmap != NULL);
874 switch (context->vclass) {
875 case TrueColor:
876 ximg = image2TrueColor(context, image);
877 break;
879 case PseudoColor:
880 case StaticColor:
881 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap)
882 ximg = image2StandardPseudoColor(context, image);
883 else
884 ximg = image2PseudoColor(context, image);
885 break;
887 case GrayScale:
888 case StaticGray:
889 ximg = image2GrayScale(context, image);
890 break;
893 if (!ximg) {
894 return False;
897 *pixmap = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, context->depth);
899 #ifdef USE_XSHM
900 if (context->flags.use_shared_pixmap && ximg->is_shared)
901 tmp = R_CreateXImageMappedPixmap(context, ximg);
902 else
903 tmp = None;
904 if (tmp) {
906 * We have to copy the shm Pixmap into a normal Pixmap because
907 * otherwise, we would have to control when Pixmaps are freed so
908 * that we can detach their shm segments. This is a problem if the
909 * program crash, leaving stale shared memory segments in the
910 * system (lots of them). But with some work, we can optimize
911 * things and remove this XCopyArea. This will require
912 * explicitly freeing all pixmaps when exiting or restarting
913 * wmaker.
915 XCopyArea(context->dpy, tmp, *pixmap, context->copy_gc, 0, 0, image->width, image->height, 0, 0);
916 XFreePixmap(context->dpy, tmp);
917 } else {
918 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
920 #else /* !USE_XSHM */
921 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
922 #endif /* !USE_XSHM */
924 RDestroyXImage(context, ximg);
926 return True;
929 /* make the gc permanent (create with context creation).
930 * GC creation is very expensive. altering its properties is not. -Dan
932 int RConvertImageMask(RContext * context, RImage * image, Pixmap * pixmap, Pixmap * mask, int threshold)
934 GC gc;
935 XGCValues gcv;
936 RXImage *ximg = NULL;
938 assert(context != NULL);
939 assert(image != NULL);
940 assert(pixmap != NULL);
941 assert(mask != NULL);
943 if (!RConvertImage(context, image, pixmap))
944 return False;
946 if (image->format == RRGBFormat) {
947 *mask = None;
948 return True;
951 ximg = image2Bitmap(context, image, threshold);
953 if (!ximg) {
954 return False;
956 *mask = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, 1);
957 gcv.foreground = context->black;
958 gcv.background = context->white;
959 gcv.graphics_exposures = False;
960 gc = XCreateGC(context->dpy, *mask, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
961 RPutXImage(context, *mask, gc, ximg, 0, 0, 0, 0, image->width, image->height);
962 RDestroyXImage(context, ximg);
963 XFreeGC(context->dpy, gc);
965 return True;
968 Bool RGetClosestXColor(RContext * context, const RColor * color, XColor * retColor)
970 if (context->vclass == TrueColor) {
971 unsigned short rmask, gmask, bmask;
972 unsigned short roffs, goffs, boffs;
973 unsigned short *rtable, *gtable, *btable;
975 roffs = context->red_offset;
976 goffs = context->green_offset;
977 boffs = context->blue_offset;
979 rmask = context->visual->red_mask >> roffs;
980 gmask = context->visual->green_mask >> goffs;
981 bmask = context->visual->blue_mask >> boffs;
983 rtable = computeTable(rmask);
984 gtable = computeTable(gmask);
985 btable = computeTable(bmask);
987 retColor->pixel = (((unsigned long) rtable[color->red]) << roffs)
988 | (((unsigned long) gtable[color->green]) << goffs)
989 | (((unsigned long) btable[color->blue]) << boffs);
991 retColor->red = color->red << 8;
992 retColor->green = color->green << 8;
993 retColor->blue = color->blue << 8;
994 retColor->flags = DoRed | DoGreen | DoBlue;
996 } else if (context->vclass == PseudoColor || context->vclass == StaticColor) {
998 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
999 unsigned int *rtable, *gtable, *btable;
1001 rtable = computeStdTable(context->std_rgb_map->red_mult, context->std_rgb_map->red_max);
1003 gtable = computeStdTable(context->std_rgb_map->green_mult,
1004 context->std_rgb_map->green_max);
1006 btable = computeStdTable(context->std_rgb_map->blue_mult, context->std_rgb_map->blue_max);
1008 if (rtable == NULL || gtable == NULL || btable == NULL) {
1009 RErrorCode = RERR_NOMEMORY;
1010 return False;
1013 retColor->pixel = (rtable[color->red]
1014 + gtable[color->green]
1015 + btable[color->blue]
1016 + context->std_rgb_map->base_pixel) & 0xffffffff;
1017 retColor->red = color->red << 8;
1018 retColor->green = color->green << 8;
1019 retColor->blue = color->blue << 8;
1020 retColor->flags = DoRed | DoGreen | DoBlue;
1022 } else {
1023 const int cpc = context->attribs->colors_per_channel;
1024 const unsigned short rmask = cpc - 1; /* different sizes could be used */
1025 const unsigned short gmask = rmask; /* for r,g,b */
1026 const unsigned short bmask = rmask;
1027 unsigned short *rtable, *gtable, *btable;
1028 const int cpccpc = cpc * cpc;
1029 int index;
1031 rtable = computeTable(rmask);
1032 gtable = computeTable(gmask);
1033 btable = computeTable(bmask);
1035 if (rtable == NULL || gtable == NULL || btable == NULL) {
1036 RErrorCode = RERR_NOMEMORY;
1037 return False;
1039 index = rtable[color->red] * cpccpc + gtable[color->green] * cpc + btable[color->blue];
1040 *retColor = context->colors[index];
1043 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
1045 const int cpc = context->attribs->colors_per_channel;
1046 unsigned short gmask;
1047 unsigned short *table;
1048 int index;
1050 if (context->vclass == StaticGray)
1051 gmask = (1 << context->depth) - 1; /* use all grays */
1052 else
1053 gmask = cpc * cpc * cpc - 1;
1055 table = computeTable(gmask);
1056 if (!table)
1057 return False;
1059 index = table[(color->red * 30 + color->green * 59 + color->blue * 11) / 100];
1061 *retColor = context->colors[index];
1062 } else {
1063 RErrorCode = RERR_INTERNAL;
1064 return False;
1067 return True;