Left Half / Right Half Maximize
[wmaker-crm.git] / wrlib / convert.c
blob46519e20c8630d34aec2a5886e16c780ce083d3d
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., 675 Mass Ave, Cambridge, MA 02139, USA.
22 /* Problems:
23 * 1. Using Grayscale visual with Dithering crashes wmaker
24 * 2. Ghost dock/appicon is wrong in Pseudocolor, Staticgray, Grayscale
27 #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>
35 #include <assert.h>
37 #ifdef BENCH
38 #include "bench.h"
39 #endif
41 #include "wraster.h"
43 #ifdef XSHM
44 extern Pixmap R_CreateXImageMappedPixmap(RContext * context, RXImage * ximage);
46 #endif
48 #ifdef ASM_X86
49 extern void x86_PseudoColor_32_to_8(unsigned char *image,
50 unsigned char *ximage,
51 char *err, char *nerr,
52 short *ctable,
53 int dr, int dg, int db,
54 unsigned long *pixels,
55 int cpc, int width, int height, int bytesPerPixel, int line_offset);
56 #endif /* ASM_X86 */
58 #ifdef ASM_X86_MMX
60 extern int x86_check_mmx();
62 extern void x86_mmx_TrueColor_32_to_16(unsigned char *image,
63 unsigned short *ximage,
64 short *err, short *nerr,
65 const unsigned short *rtable,
66 const unsigned short *gtable,
67 const unsigned short *btable,
68 int dr, int dg, int db,
69 unsigned int roffs,
70 unsigned int goffs,
71 unsigned int boffs, int width, int height, int line_offset);
73 #endif /* ASM_X86_MMX */
75 #define NFREE(n) if (n) free(n)
77 #define HAS_ALPHA(I) ((I)->format == RRGBAFormat)
79 typedef struct RConversionTable {
80 unsigned short table[256];
81 unsigned short index;
83 struct RConversionTable *next;
84 } RConversionTable;
86 typedef struct RStdConversionTable {
87 unsigned int table[256];
89 unsigned short mult;
90 unsigned short max;
92 struct RStdConversionTable *next;
93 } RStdConversionTable;
95 static RConversionTable *conversionTable = NULL;
96 static RStdConversionTable *stdConversionTable = NULL;
98 static unsigned short *computeTable(unsigned short mask)
100 RConversionTable *tmp = conversionTable;
101 int i;
103 while (tmp) {
104 if (tmp->index == mask)
105 break;
106 tmp = tmp->next;
109 if (tmp)
110 return tmp->table;
112 tmp = (RConversionTable *) malloc(sizeof(RConversionTable));
113 if (tmp == NULL)
114 return NULL;
116 for (i = 0; i < 256; i++)
117 tmp->table[i] = (i * mask + 0x7f) / 0xff;
119 tmp->index = mask;
120 tmp->next = conversionTable;
121 conversionTable = tmp;
122 return tmp->table;
125 static unsigned int *computeStdTable(unsigned int mult, unsigned int max)
127 RStdConversionTable *tmp = stdConversionTable;
128 unsigned int i;
130 while (tmp) {
131 if (tmp->mult == mult && tmp->max == max)
132 break;
133 tmp = tmp->next;
136 if (tmp)
137 return tmp->table;
139 tmp = (RStdConversionTable *) malloc(sizeof(RStdConversionTable));
140 if (tmp == NULL)
141 return NULL;
143 for (i = 0; i < 256; i++) {
144 tmp->table[i] = (i * max) / 0xff * mult;
146 tmp->mult = mult;
147 tmp->max = max;
149 tmp->next = stdConversionTable;
150 stdConversionTable = tmp;
152 return tmp->table;
155 /***************************************************************************/
157 static void
158 convertTrueColor_generic(RXImage * ximg, RImage * image,
159 signed char *err, signed char *nerr,
160 const unsigned short *rtable,
161 const unsigned short *gtable,
162 const unsigned short *btable,
163 const int dr, const int dg, const int db,
164 const unsigned short roffs, const unsigned short goffs, const unsigned short boffs)
166 signed char *terr;
167 int x, y, r, g, b;
168 int pixel;
169 int rer, ger, ber;
170 unsigned char *ptr = image->data;
171 int channels = (HAS_ALPHA(image) ? 4 : 3);
173 /* convert and dither the image to XImage */
174 for (y = 0; y < image->height; y++) {
175 nerr[0] = 0;
176 nerr[1] = 0;
177 nerr[2] = 0;
178 for (x = 0; x < image->width; x++, ptr += channels) {
180 /* reduce pixel */
181 pixel = *ptr + err[x];
182 if (pixel < 0)
183 pixel = 0;
184 else if (pixel > 0xff)
185 pixel = 0xff;
186 r = rtable[pixel];
187 /* calc error */
188 rer = pixel - r * dr;
190 /* reduce pixel */
191 pixel = *(ptr + 1) + err[x + 1];
192 if (pixel < 0)
193 pixel = 0;
194 else if (pixel > 0xff)
195 pixel = 0xff;
196 g = gtable[pixel];
197 /* calc error */
198 ger = pixel - g * dg;
200 /* reduce pixel */
201 pixel = *(ptr + 2) + err[x + 2];
202 if (pixel < 0)
203 pixel = 0;
204 else if (pixel > 0xff)
205 pixel = 0xff;
206 b = btable[pixel];
207 /* calc error */
208 ber = pixel - b * db;
210 pixel = (r << roffs) | (g << goffs) | (b << boffs);
211 XPutPixel(ximg->image, x, y, pixel);
213 /* distribute error */
214 r = (rer * 3) / 8;
215 g = (ger * 3) / 8;
216 b = (ber * 3) / 8;
217 /* x+1, y */
218 err[x + 3 * 1] += r;
219 err[x + 1 + 3 * 1] += g;
220 err[x + 2 + 3 * 1] += b;
221 /* x, y+1 */
222 nerr[x] += r;
223 nerr[x + 1] += g;
224 nerr[x + 2] += b;
225 /* x+1, y+1 */
226 nerr[x + 3 * 1] = rer - 2 * r;
227 nerr[x + 1 + 3 * 1] = ger - 2 * g;
228 nerr[x + 2 + 3 * 1] = ber - 2 * b;
230 /* skip to next line */
231 terr = err;
232 err = nerr;
233 nerr = terr;
236 /* redither the 1st line to distribute error better */
237 ptr = image->data;
238 y = 0;
239 nerr[0] = 0;
240 nerr[1] = 0;
241 nerr[2] = 0;
242 for (x = 0; x < image->width; x++, ptr += channels) {
244 /* reduce pixel */
245 pixel = *ptr + err[x];
246 if (pixel < 0)
247 pixel = 0;
248 else if (pixel > 0xff)
249 pixel = 0xff;
250 r = rtable[pixel];
251 /* calc error */
252 rer = pixel - r * dr;
254 /* reduce pixel */
255 pixel = *(ptr + 1) + err[x + 1];
256 if (pixel < 0)
257 pixel = 0;
258 else if (pixel > 0xff)
259 pixel = 0xff;
260 g = gtable[pixel];
261 /* calc error */
262 ger = pixel - g * dg;
264 /* reduce pixel */
265 pixel = *(ptr + 2) + err[x + 2];
266 if (pixel < 0)
267 pixel = 0;
268 else if (pixel > 0xff)
269 pixel = 0xff;
270 b = btable[pixel];
271 /* calc error */
272 ber = pixel - b * db;
274 pixel = (r << roffs) | (g << goffs) | (b << boffs);
275 XPutPixel(ximg->image, x, y, pixel);
277 /* distribute error */
278 r = (rer * 3) / 8;
279 g = (ger * 3) / 8;
280 b = (ber * 3) / 8;
281 /* x+1, y */
282 err[x + 3 * 1] += r;
283 err[x + 1 + 3 * 1] += g;
284 err[x + 2 + 3 * 1] += b;
285 /* x, y+1 */
286 nerr[x] += r;
287 nerr[x + 1] += g;
288 nerr[x + 2] += b;
289 /* x+1, y+1 */
290 nerr[x + 3 * 1] = rer - 2 * r;
291 nerr[x + 1 + 3 * 1] = ger - 2 * g;
292 nerr[x + 2 + 3 * 1] = ber - 2 * b;
296 static RXImage *image2TrueColor(RContext * ctx, RImage * image)
298 RXImage *ximg;
299 unsigned short rmask, gmask, bmask;
300 unsigned short roffs, goffs, boffs;
301 unsigned short *rtable, *gtable, *btable;
302 int channels = (HAS_ALPHA(image) ? 4 : 3);
304 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
305 if (!ximg) {
306 return NULL;
309 roffs = ctx->red_offset;
310 goffs = ctx->green_offset;
311 boffs = ctx->blue_offset;
313 rmask = ctx->visual->red_mask >> roffs;
314 gmask = ctx->visual->green_mask >> goffs;
315 bmask = ctx->visual->blue_mask >> boffs;
317 rtable = computeTable(rmask);
318 gtable = computeTable(gmask);
319 btable = computeTable(bmask);
321 if (rtable == NULL || gtable == NULL || btable == NULL) {
322 RErrorCode = RERR_NOMEMORY;
323 RDestroyXImage(ctx, ximg);
324 return NULL;
327 #ifdef BENCH
328 cycle_bench(1);
329 #endif
331 if (ctx->attribs->render_mode == RBestMatchRendering) {
332 int ofs, r, g, b;
333 int x, y;
334 unsigned long pixel;
335 unsigned char *ptr = image->data;
337 /* fake match */
338 #ifdef DEBUG
339 puts("true color match");
340 #endif
341 if (rmask == 0xff && gmask == 0xff && bmask == 0xff) {
342 for (y = 0; y < image->height; y++) {
343 for (x = 0; x < image->width; x++, ptr += channels) {
344 /* reduce pixel */
345 pixel = (*(ptr) << roffs) | (*(ptr + 1) << goffs) | (*(ptr + 2) << boffs);
346 XPutPixel(ximg->image, x, y, pixel);
349 } else {
350 for (y = 0, ofs = 0; y < image->height; y++) {
351 for (x = 0; x < image->width; x++, ofs += channels - 3) {
352 /* reduce pixel */
353 r = rtable[ptr[ofs++]];
354 g = gtable[ptr[ofs++]];
355 b = btable[ptr[ofs++]];
356 pixel = (r << roffs) | (g << goffs) | (b << boffs);
357 XPutPixel(ximg->image, x, y, pixel);
361 } else {
362 /* dither */
363 const int dr = 0xff / rmask;
364 const int dg = 0xff / gmask;
365 const int db = 0xff / bmask;
367 #ifdef DEBUG
368 puts("true color dither");
369 #endif
371 #ifdef ASM_X86_MMX
372 if (ctx->depth == 16 && HAS_ALPHA(image) && x86_check_mmx()) {
373 short *err;
374 short *nerr;
376 err = malloc(8 * (image->width + 3));
377 nerr = malloc(8 * (image->width + 3));
378 if (!err || !nerr) {
379 NFREE(err);
380 NFREE(nerr);
381 RErrorCode = RERR_NOMEMORY;
382 RDestroyXImage(ctx, ximg);
383 return NULL;
385 memset(err, 0, 8 * (image->width + 3));
386 memset(nerr, 0, 8 * (image->width + 3));
388 x86_mmx_TrueColor_32_to_16(image->data,
389 (unsigned short *)ximg->image->data,
390 err + 8, nerr + 8,
391 rtable, gtable, btable,
392 dr, dg, db,
393 roffs, goffs, boffs,
394 image->width, image->height,
395 ximg->image->bytes_per_line - 2 * image->width);
397 free(err);
398 free(nerr);
399 } else
400 #endif /* ASM_X86_MMX */
402 signed char *err;
403 signed char *nerr;
404 int ch = (HAS_ALPHA(image) ? 4 : 3);
406 err = malloc(ch * (image->width + 2));
407 nerr = malloc(ch * (image->width + 2));
408 if (!err || !nerr) {
409 NFREE(err);
410 NFREE(nerr);
411 RErrorCode = RERR_NOMEMORY;
412 RDestroyXImage(ctx, ximg);
413 return NULL;
416 memset(err, 0, ch * (image->width + 2));
417 memset(nerr, 0, ch * (image->width + 2));
419 convertTrueColor_generic(ximg, image, err, nerr,
420 rtable, gtable, btable, dr, dg, db, roffs, goffs, boffs);
421 free(err);
422 free(nerr);
427 #ifdef BENCH
428 cycle_bench(0);
429 #endif
431 return ximg;
434 /***************************************************************************/
436 static void
437 convertPseudoColor_to_8(RXImage * ximg, RImage * image,
438 signed char *err, signed char *nerr,
439 const unsigned short *rtable,
440 const unsigned short *gtable,
441 const unsigned short *btable,
442 const int dr, const int dg, const int db, unsigned long *pixels, int cpc)
444 signed char *terr;
445 int x, y, r, g, b;
446 int pixel;
447 int rer, ger, ber;
448 unsigned char *ptr = image->data;
449 unsigned char *optr = (unsigned char *)ximg->image->data;
450 int channels = (HAS_ALPHA(image) ? 4 : 3);
451 int cpcpc = cpc * cpc;
453 /* convert and dither the image to XImage */
454 for (y = 0; y < image->height; y++) {
455 nerr[0] = 0;
456 nerr[1] = 0;
457 nerr[2] = 0;
458 for (x = 0; x < image->width * 3; x += 3, ptr += channels) {
460 /* reduce pixel */
461 pixel = *ptr + err[x];
462 if (pixel < 0)
463 pixel = 0;
464 else if (pixel > 0xff)
465 pixel = 0xff;
466 r = rtable[pixel];
467 /* calc error */
468 rer = pixel - r * dr;
470 /* reduce pixel */
471 pixel = *(ptr + 1) + err[x + 1];
472 if (pixel < 0)
473 pixel = 0;
474 else if (pixel > 0xff)
475 pixel = 0xff;
476 g = gtable[pixel];
477 /* calc error */
478 ger = pixel - g * dg;
480 /* reduce pixel */
481 pixel = *(ptr + 2) + err[x + 2];
482 if (pixel < 0)
483 pixel = 0;
484 else if (pixel > 0xff)
485 pixel = 0xff;
486 b = btable[pixel];
487 /* calc error */
488 ber = pixel - b * db;
490 *optr++ = pixels[r * cpcpc + g * cpc + b];
492 /* distribute error */
493 r = (rer * 3) / 8;
494 g = (ger * 3) / 8;
495 b = (ber * 3) / 8;
497 /* x+1, y */
498 err[x + 3 * 1] += r;
499 err[x + 1 + 3 * 1] += g;
500 err[x + 2 + 3 * 1] += b;
501 /* x, y+1 */
502 nerr[x] += r;
503 nerr[x + 1] += g;
504 nerr[x + 2] += b;
505 /* x+1, y+1 */
506 nerr[x + 3 * 1] = rer - 2 * r;
507 nerr[x + 1 + 3 * 1] = ger - 2 * g;
508 nerr[x + 2 + 3 * 1] = ber - 2 * b;
510 /* skip to next line */
511 terr = err;
512 err = nerr;
513 nerr = terr;
515 optr += ximg->image->bytes_per_line - image->width;
519 static RXImage *image2PseudoColor(RContext * ctx, RImage * image)
521 RXImage *ximg;
522 register int x, y, r, g, b;
523 unsigned char *ptr;
524 unsigned long pixel;
525 const int cpc = ctx->attribs->colors_per_channel;
526 const unsigned short rmask = cpc - 1; /* different sizes could be used */
527 const unsigned short gmask = rmask; /* for r,g,b */
528 const unsigned short bmask = rmask;
529 unsigned short *rtable, *gtable, *btable;
530 const int cpccpc = cpc * cpc;
531 int channels = (HAS_ALPHA(image) ? 4 : 3);
533 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
534 if (!ximg) {
535 return NULL;
538 ptr = image->data;
540 /* Tables are same at the moment because rmask==gmask==bmask. */
541 rtable = computeTable(rmask);
542 gtable = computeTable(gmask);
543 btable = computeTable(bmask);
545 if (rtable == NULL || gtable == NULL || btable == NULL) {
546 RErrorCode = RERR_NOMEMORY;
547 RDestroyXImage(ctx, ximg);
548 return NULL;
551 if (ctx->attribs->render_mode == RBestMatchRendering) {
552 /* fake match */
553 #ifdef DEBUG
554 printf("pseudo color match with %d colors per channel\n", cpc);
555 #endif
556 for (y = 0; y < image->height; y++) {
557 for (x = 0; x < image->width; x++, ptr += channels - 3) {
558 /* reduce pixel */
559 r = rtable[*ptr++];
560 g = gtable[*ptr++];
561 b = btable[*ptr++];
562 pixel = r * cpccpc + g * cpc + b;
563 /*data[ofs] = ctx->colors[pixel].pixel; */
564 XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel);
567 } else {
568 /* dither */
569 signed char *err;
570 signed char *nerr;
571 const int dr = 0xff / rmask;
572 const int dg = 0xff / gmask;
573 const int db = 0xff / bmask;
575 #ifdef DEBUG
576 printf("pseudo color dithering with %d colors per channel\n", cpc);
577 #endif
578 err = malloc(4 * (image->width + 3));
579 nerr = malloc(4 * (image->width + 3));
580 if (!err || !nerr) {
581 NFREE(err);
582 NFREE(nerr);
583 RErrorCode = RERR_NOMEMORY;
584 RDestroyXImage(ctx, ximg);
585 return NULL;
587 memset(err, 0, 4 * (image->width + 3));
588 memset(nerr, 0, 4 * (image->width + 3));
590 /*#ifdef ASM_X86 */
591 #if 0
592 x86_PseudoColor_32_to_8(image->data, ximg->image->data,
593 err + 4, nerr + 4,
594 rtable,
595 dr, dg, db, ctx->pixels, cpc,
596 image->width, image->height,
597 channels, ximg->image->bytes_per_line - image->width);
598 #else
599 convertPseudoColor_to_8(ximg, image, err + 4, nerr + 4,
600 rtable, gtable, btable, dr, dg, db, ctx->pixels, cpc);
601 #endif
603 free(err);
604 free(nerr);
607 return ximg;
611 * For standard colormap
613 static RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image)
615 RXImage *ximg;
616 register int x, y, r, g, b;
617 unsigned char *ptr;
618 unsigned long pixel;
619 unsigned char *data;
620 unsigned int *rtable, *gtable, *btable;
621 unsigned int base_pixel = ctx->std_rgb_map->base_pixel;
622 int channels = (HAS_ALPHA(image) ? 4 : 3);
624 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
625 if (!ximg) {
626 return NULL;
629 ptr = image->data;
631 data = (unsigned char *)ximg->image->data;
633 rtable = computeStdTable(ctx->std_rgb_map->red_mult, ctx->std_rgb_map->red_max);
635 gtable = computeStdTable(ctx->std_rgb_map->green_mult, ctx->std_rgb_map->green_max);
637 btable = computeStdTable(ctx->std_rgb_map->blue_mult, ctx->std_rgb_map->blue_max);
639 if (rtable == NULL || gtable == NULL || btable == NULL) {
640 RErrorCode = RERR_NOMEMORY;
641 RDestroyXImage(ctx, ximg);
642 return NULL;
645 if (ctx->attribs->render_mode == RBestMatchRendering) {
646 for (y = 0; y < image->height; y++) {
647 for (x = 0; x < image->width; x++, ptr += channels) {
648 /* reduce pixel */
649 pixel = (rtable[*ptr] + gtable[*(ptr + 1)]
650 + btable[*(ptr + 2)] + base_pixel) & 0xffffffff;
652 XPutPixel(ximg->image, x, y, pixel);
655 } else {
656 /* dither */
657 signed short *err, *nerr;
658 signed short *terr;
659 int rer, ger, ber;
660 int x1, ofs;
662 #ifdef DEBUG
663 printf("pseudo color dithering with %d colors per channel\n", ctx->attribs->colors_per_channel);
664 #endif
665 err = (short *)malloc(3 * (image->width + 2) * sizeof(short));
666 nerr = (short *)malloc(3 * (image->width + 2) * sizeof(short));
667 if (!err || !nerr) {
668 NFREE(err);
669 NFREE(nerr);
670 RErrorCode = RERR_NOMEMORY;
671 RDestroyXImage(ctx, ximg);
672 return NULL;
674 for (x = 0, x1 = 0; x < image->width * 3; x1 += channels - 3) {
675 err[x++] = ptr[x1++];
676 err[x++] = ptr[x1++];
677 err[x++] = ptr[x1++];
679 err[x] = err[x + 1] = err[x + 2] = 0;
680 /* convert and dither the image to XImage */
681 for (y = 0, ofs = 0; y < image->height; y++) {
682 if (y < image->height - 1) {
683 int x1;
684 for (x = 0, x1 = (y + 1) * image->width * channels;
685 x < image->width * 3; x1 += channels - 3) {
686 nerr[x++] = ptr[x1++];
687 nerr[x++] = ptr[x1++];
688 nerr[x++] = ptr[x1++];
690 /* last column */
691 x1 -= channels;
692 nerr[x++] = ptr[x1++];
693 nerr[x++] = ptr[x1++];
694 nerr[x++] = ptr[x1++];
696 for (x = 0; x < image->width * 3; x += 3, ofs++) {
697 /* reduce pixel */
698 if (err[x] > 0xff)
699 err[x] = 0xff;
700 else if (err[x] < 0)
701 err[x] = 0;
702 if (err[x + 1] > 0xff)
703 err[x + 1] = 0xff;
704 else if (err[x + 1] < 0)
705 err[x + 1] = 0;
706 if (err[x + 2] > 0xff)
707 err[x + 2] = 0xff;
708 else if (err[x + 2] < 0)
709 err[x + 2] = 0;
711 r = rtable[err[x]];
712 g = gtable[err[x + 1]];
713 b = btable[err[x + 2]];
715 pixel = r + g + b;
717 data[ofs] = base_pixel + pixel;
719 /* calc error */
720 rer = err[x] - (ctx->colors[pixel].red >> 8);
721 ger = err[x + 1] - (ctx->colors[pixel].green >> 8);
722 ber = err[x + 2] - (ctx->colors[pixel].blue >> 8);
724 /* distribute error */
725 err[x + 3 * 1] += (rer * 7) / 16;
726 err[x + 1 + 3 * 1] += (ger * 7) / 16;
727 err[x + 2 + 3 * 1] += (ber * 7) / 16;
729 nerr[x] += (rer * 5) / 16;
730 nerr[x + 1] += (ger * 5) / 16;
731 nerr[x + 2] += (ber * 5) / 16;
733 if (x > 0) {
734 nerr[x - 3 * 1] += (rer * 3) / 16;
735 nerr[x - 3 * 1 + 1] += (ger * 3) / 16;
736 nerr[x - 3 * 1 + 2] += (ber * 3) / 16;
739 nerr[x + 3 * 1] += rer / 16;
740 nerr[x + 1 + 3 * 1] += ger / 16;
741 nerr[x + 2 + 3 * 1] += ber / 16;
743 /* skip to next line */
744 terr = err;
745 err = nerr;
746 nerr = terr;
748 ofs += ximg->image->bytes_per_line - image->width;
750 free(err);
751 free(nerr);
753 ximg->image->data = (char *)data;
755 return ximg;
758 static RXImage *image2GrayScale(RContext * ctx, RImage * image)
760 RXImage *ximg;
761 register int x, y, g;
762 unsigned char *ptr;
763 const int cpc = ctx->attribs->colors_per_channel;
764 unsigned short gmask;
765 unsigned short *table;
766 unsigned char *data;
767 int channels = (HAS_ALPHA(image) ? 4 : 3);
769 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
770 if (!ximg) {
771 return NULL;
774 ptr = image->data;
776 data = (unsigned char *)ximg->image->data;
778 if (ctx->vclass == StaticGray)
779 gmask = (1 << ctx->depth) - 1; /* use all grays */
780 else
781 gmask = cpc * cpc * cpc - 1;
783 table = computeTable(gmask);
785 if (table == NULL) {
786 RErrorCode = RERR_NOMEMORY;
787 RDestroyXImage(ctx, ximg);
788 return NULL;
791 if (ctx->attribs->render_mode == RBestMatchRendering) {
792 /* fake match */
793 #ifdef DEBUG
794 printf("grayscale match with %d colors per channel\n", cpc);
795 #endif
796 for (y = 0; y < image->height; y++) {
797 for (x = 0; x < image->width; x++) {
798 /* reduce pixel */
799 g = table[(*ptr * 30 + *(ptr + 1) * 59 + *(ptr + 2) * 11) / 100];
800 ptr += channels;
801 /*data[ofs] = ctx->colors[g].pixel; */
802 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
805 } else {
806 /* dither */
807 short *gerr;
808 short *ngerr;
809 short *terr;
810 int ger;
811 const int dg = 0xff / gmask;
813 #ifdef DEBUG
814 printf("grayscale dither with %d colors per channel\n", cpc);
815 #endif
816 gerr = (short *)malloc((image->width + 2) * sizeof(short));
817 ngerr = (short *)malloc((image->width + 2) * sizeof(short));
818 if (!gerr || !ngerr) {
819 NFREE(gerr);
820 NFREE(ngerr);
821 RErrorCode = RERR_NOMEMORY;
822 RDestroyXImage(ctx, ximg);
823 return NULL;
825 for (x = 0, y = 0; x < image->width; x++, y += channels) {
826 gerr[x] = (ptr[y] * 30 + ptr[y + 1] * 59 + ptr[y + 2] * 11) / 100;
828 gerr[x] = 0;
829 /* convert and dither the image to XImage */
830 for (y = 0; y < image->height; y++) {
831 if (y < image->height - 1) {
832 int x1;
833 for (x = 0, x1 = (y + 1) * image->width * channels; x < image->width;
834 x++, x1 += channels) {
835 ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100;
837 /* last column */
838 x1 -= channels;
839 ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100;
841 for (x = 0; x < image->width; x++) {
842 /* reduce pixel */
843 if (gerr[x] > 0xff)
844 gerr[x] = 0xff;
845 else if (gerr[x] < 0)
846 gerr[x] = 0;
848 g = table[gerr[x]];
850 /*data[ofs] = ctx->colors[g].pixel; */
851 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
852 /* calc error */
853 ger = gerr[x] - g * dg;
855 /* distribute error */
856 g = (ger * 3) / 8;
857 /* x+1, y */
858 gerr[x + 1] += g;
859 /* x, y+1 */
860 ngerr[x] += g;
861 /* x+1, y+1 */
862 ngerr[x + 1] += ger - 2 * g;
864 /* skip to next line */
865 terr = gerr;
866 gerr = ngerr;
867 ngerr = terr;
869 free(gerr);
870 free(ngerr);
872 ximg->image->data = (char *)data;
874 return ximg;
877 static RXImage *image2Bitmap(RContext * ctx, RImage * image, int threshold)
879 RXImage *ximg;
880 unsigned char *alpha;
881 int x, y;
883 ximg = RCreateXImage(ctx, 1, image->width, image->height);
884 if (!ximg) {
885 return NULL;
887 alpha = image->data + 3;
889 for (y = 0; y < image->height; y++) {
890 for (x = 0; x < image->width; x++) {
891 XPutPixel(ximg->image, x, y, (*alpha <= threshold ? 0 : 1));
892 alpha += 4;
896 return ximg;
899 int RConvertImage(RContext * context, RImage * image, Pixmap * pixmap)
901 RXImage *ximg = NULL;
902 #ifdef XSHM
903 Pixmap tmp;
904 #endif
906 assert(context != NULL);
907 assert(image != NULL);
908 assert(pixmap != NULL);
910 switch (context->vclass) {
911 case TrueColor:
912 #ifdef BENCH
913 cycle_bench(1);
914 #endif
915 ximg = image2TrueColor(context, image);
916 #ifdef BENCH
917 cycle_bench(0);
918 #endif
919 break;
921 case PseudoColor:
922 case StaticColor:
923 #ifdef BENCH
924 cycle_bench(1);
925 #endif
926 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap)
927 ximg = image2StandardPseudoColor(context, image);
928 else
929 ximg = image2PseudoColor(context, image);
930 #ifdef BENCH
931 cycle_bench(0);
932 #endif
933 break;
935 case GrayScale:
936 case StaticGray:
937 ximg = image2GrayScale(context, image);
938 break;
941 if (!ximg) {
942 return False;
945 *pixmap = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, context->depth);
947 #ifdef XSHM
948 if (context->flags.use_shared_pixmap && ximg->is_shared)
949 tmp = R_CreateXImageMappedPixmap(context, ximg);
950 else
951 tmp = None;
952 if (tmp) {
954 * We have to copy the shm Pixmap into a normal Pixmap because
955 * otherwise, we would have to control when Pixmaps are freed so
956 * that we can detach their shm segments. This is a problem if the
957 * program crash, leaving stale shared memory segments in the
958 * system (lots of them). But with some work, we can optimize
959 * things and remove this XCopyArea. This will require
960 * explicitly freeing all pixmaps when exiting or restarting
961 * wmaker.
963 XCopyArea(context->dpy, tmp, *pixmap, context->copy_gc, 0, 0, image->width, image->height, 0, 0);
964 XFreePixmap(context->dpy, tmp);
965 } else {
966 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
968 #else /* !XSHM */
969 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
970 #endif /* !XSHM */
972 RDestroyXImage(context, ximg);
974 return True;
977 /* make the gc permanent (create with context creation).
978 * GC creation is very expensive. altering its properties is not. -Dan
980 int RConvertImageMask(RContext * context, RImage * image, Pixmap * pixmap, Pixmap * mask, int threshold)
982 GC gc;
983 XGCValues gcv;
984 RXImage *ximg = NULL;
986 assert(context != NULL);
987 assert(image != NULL);
988 assert(pixmap != NULL);
989 assert(mask != NULL);
991 if (!RConvertImage(context, image, pixmap))
992 return False;
994 if (image->format == RRGBFormat) {
995 *mask = None;
996 return True;
999 ximg = image2Bitmap(context, image, threshold);
1001 if (!ximg) {
1002 return False;
1004 *mask = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, 1);
1005 gcv.foreground = context->black;
1006 gcv.background = context->white;
1007 gcv.graphics_exposures = False;
1008 gc = XCreateGC(context->dpy, *mask, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
1009 RPutXImage(context, *mask, gc, ximg, 0, 0, 0, 0, image->width, image->height);
1010 RDestroyXImage(context, ximg);
1011 XFreeGC(context->dpy, gc);
1013 return True;
1016 Bool RGetClosestXColor(RContext * context, RColor * color, XColor * retColor)
1018 if (context->vclass == TrueColor) {
1019 unsigned short rmask, gmask, bmask;
1020 unsigned short roffs, goffs, boffs;
1021 unsigned short *rtable, *gtable, *btable;
1023 roffs = context->red_offset;
1024 goffs = context->green_offset;
1025 boffs = context->blue_offset;
1027 rmask = context->visual->red_mask >> roffs;
1028 gmask = context->visual->green_mask >> goffs;
1029 bmask = context->visual->blue_mask >> boffs;
1031 rtable = computeTable(rmask);
1032 gtable = computeTable(gmask);
1033 btable = computeTable(bmask);
1035 retColor->pixel = (rtable[color->red] << roffs) |
1036 (gtable[color->green] << goffs) | (btable[color->blue] << boffs);
1038 retColor->red = color->red << 8;
1039 retColor->green = color->green << 8;
1040 retColor->blue = color->blue << 8;
1041 retColor->flags = DoRed | DoGreen | DoBlue;
1043 } else if (context->vclass == PseudoColor || context->vclass == StaticColor) {
1045 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
1046 unsigned int *rtable, *gtable, *btable;
1048 rtable = computeStdTable(context->std_rgb_map->red_mult, context->std_rgb_map->red_max);
1050 gtable = computeStdTable(context->std_rgb_map->green_mult,
1051 context->std_rgb_map->green_max);
1053 btable = computeStdTable(context->std_rgb_map->blue_mult, context->std_rgb_map->blue_max);
1055 if (rtable == NULL || gtable == NULL || btable == NULL) {
1056 RErrorCode = RERR_NOMEMORY;
1057 return False;
1060 retColor->pixel = (rtable[color->red]
1061 + gtable[color->green]
1062 + btable[color->blue]
1063 + context->std_rgb_map->base_pixel) & 0xffffffff;
1064 retColor->red = color->red << 8;
1065 retColor->green = color->green << 8;
1066 retColor->blue = color->blue << 8;
1067 retColor->flags = DoRed | DoGreen | DoBlue;
1069 } else {
1070 const int cpc = context->attribs->colors_per_channel;
1071 const unsigned short rmask = cpc - 1; /* different sizes could be used */
1072 const unsigned short gmask = rmask; /* for r,g,b */
1073 const unsigned short bmask = rmask;
1074 unsigned short *rtable, *gtable, *btable;
1075 const int cpccpc = cpc * cpc;
1076 int index;
1078 rtable = computeTable(rmask);
1079 gtable = computeTable(gmask);
1080 btable = computeTable(bmask);
1082 if (rtable == NULL || gtable == NULL || btable == NULL) {
1083 RErrorCode = RERR_NOMEMORY;
1084 return False;
1086 index = rtable[color->red] * cpccpc + gtable[color->green] * cpc + btable[color->blue];
1087 *retColor = context->colors[index];
1090 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
1092 const int cpc = context->attribs->colors_per_channel;
1093 unsigned short gmask;
1094 unsigned short *table;
1095 int index;
1097 if (context->vclass == StaticGray)
1098 gmask = (1 << context->depth) - 1; /* use all grays */
1099 else
1100 gmask = cpc * cpc * cpc - 1;
1102 table = computeTable(gmask);
1103 if (!table)
1104 return False;
1106 index = table[(color->red * 30 + color->green * 59 + color->blue * 11) / 100];
1108 *retColor = context->colors[index];
1109 } else {
1110 RErrorCode = RERR_INTERNAL;
1111 return False;
1114 return True;