Updating to version 0.20.2
[wmaker-crm.git] / wrlib / convert.c
blob29029d309be63264190848c1f5f737e3d54550a1
1 /* convert.c - convert RImage to Pixmap
2 *
3 * Raster graphics library
5 * Copyright (c) 1997 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.
21 #include <config.h>
23 /* AIX requires this to be the first thing in the file. */
24 #ifdef __GNUC__
25 # define alloca __builtin_alloca
26 #else
27 # if HAVE_ALLOCA_H
28 # include <alloca.h>
29 # else
30 # ifdef _AIX
31 # pragma alloca
32 # else
33 # ifndef alloca /* predefined by HP cc +Olibcalls */
34 char *alloca ();
35 # endif
36 # endif
37 # endif
38 #endif
41 #include <X11/Xlib.h>
42 #include <X11/Xutil.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
47 #include <assert.h>
49 #include "wraster.h"
51 #ifdef XSHM
52 Pixmap R_CreateXImageMappedPixmap(RContext *context, RXImage *ximage);
54 #endif
57 typedef struct RConversionTable {
58 unsigned short table[256];
59 unsigned short index;
60 struct RConversionTable *next;
61 } RConversionTable;
64 static RConversionTable *conversionTable = NULL;
67 static unsigned short*
68 computeTable(unsigned short mask)
70 RConversionTable *tmp = conversionTable;
71 int i;
73 while (tmp) {
74 if (tmp->index == mask)
75 break;
76 tmp = tmp->next;
79 if (tmp)
80 return tmp->table;
82 tmp = (RConversionTable *)malloc(sizeof(RConversionTable));
83 if (tmp == NULL)
84 return NULL;
86 for (i=0;i<256;i++)
87 tmp->table[i] = (i*mask + 0x7f)/0xff;
89 tmp->index = mask;
90 tmp->next = conversionTable;
91 conversionTable = tmp;
92 return tmp->table;
96 static RXImage*
97 image2TrueColor(RContext *ctx, RImage *image)
99 RXImage *ximg;
100 register int x, y, r, g, b;
101 unsigned char *red, *grn, *blu;
102 unsigned long pixel;
103 unsigned short rmask, gmask, bmask;
104 unsigned short roffs, goffs, boffs;
105 unsigned short *rtable, *gtable, *btable;
106 int ofs;
108 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
109 if (!ximg) {
110 return NULL;
113 red = image->data[0];
114 grn = image->data[1];
115 blu = image->data[2];
117 roffs = ctx->red_offset;
118 goffs = ctx->green_offset;
119 boffs = ctx->blue_offset;
121 rmask = ctx->visual->red_mask >> roffs;
122 gmask = ctx->visual->green_mask >> goffs;
123 bmask = ctx->visual->blue_mask >> boffs;
125 #if 0
126 /* this do not seem to increase speed. Only 0.06 second faster in
127 * rendering a 800x600 image to pixmap. 1.12 sec instead of 1.18.
128 * But does not require a 256*256*256 lookup table.
130 if (ctx->depth==24) {
131 #ifdef DEBUG
132 puts("true color match for 24bpp");
133 #endif
134 for (y=0; y < image->height; y++) {
135 for (x=0; x < image->width; x++) {
136 pixel = (*(red++)<<roffs) | (*(grn++)<<goffs) | (*(blu++)<<boffs);
137 XPutPixel(ximg->image, x, y, pixel);
140 return ximg;
142 #endif
144 rtable = computeTable(rmask);
145 gtable = computeTable(gmask);
146 btable = computeTable(bmask);
148 if (rtable==NULL || gtable==NULL || btable==NULL) {
149 RErrorCode = RERR_NOMEMORY;
150 RDestroyXImage(ctx, ximg);
151 return NULL;
154 if (ctx->attribs->render_mode==RM_MATCH) {
155 /* fake match */
156 #ifdef DEBUG
157 puts("true color match");
158 #endif
159 for (y=0, ofs=0; y < image->height; y++) {
160 for (x=0; x < image->width; x++, ofs++) {
161 /* reduce pixel */
162 r = rtable[red[ofs]];
163 g = gtable[grn[ofs]];
164 b = btable[blu[ofs]];
165 pixel = (r<<roffs) | (g<<goffs) | (b<<boffs);
166 XPutPixel(ximg->image, x, y, pixel);
169 } else {
170 /* dither */
171 short *rerr, *gerr, *berr;
172 short *nrerr, *ngerr, *nberr;
173 short *terr;
174 int rer, ger, ber;
175 const int dr=0xff/rmask;
176 const int dg=0xff/gmask;
177 const int db=0xff/bmask;
179 #ifdef DEBUG
180 puts("true color dither");
181 #endif
182 rerr = (short*)alloca((image->width+2)*sizeof(short));
183 gerr = (short*)alloca((image->width+2)*sizeof(short));
184 berr = (short*)alloca((image->width+2)*sizeof(short));
185 nrerr = (short*)alloca((image->width+2)*sizeof(short));
186 ngerr = (short*)alloca((image->width+2)*sizeof(short));
187 nberr = (short*)alloca((image->width+2)*sizeof(short));
188 if (!rerr || !gerr || !berr || !nrerr || !ngerr || !nberr) {
189 RErrorCode = RERR_NOMEMORY;
190 RDestroyXImage(ctx, ximg);
191 return NULL;
193 for (x=0; x<image->width; x++) {
194 rerr[x] = red[x];
195 gerr[x] = grn[x];
196 berr[x] = blu[x];
198 rerr[x] = gerr[x] = berr[x] = 0;
199 /* convert and dither the image to XImage */
200 for (y=0, ofs=0; y<image->height; y++) {
201 if (y<image->height-1) {
202 int x1;
203 for (x=0, x1=ofs+image->width; x<image->width; x++, x1++) {
204 nrerr[x] = red[x1];
205 ngerr[x] = grn[x1];
206 nberr[x] = blu[x1];
208 /* last column */
209 x1--;
210 nrerr[x] = red[x1];
211 ngerr[x] = grn[x1];
212 nberr[x] = blu[x1];
214 for (x=0; x<image->width; x++) {
215 /* reduce pixel */
216 if (rerr[x]>0xff) rerr[x]=0xff; else if (rerr[x]<0) rerr[x]=0;
217 if (gerr[x]>0xff) gerr[x]=0xff; else if (gerr[x]<0) gerr[x]=0;
218 if (berr[x]>0xff) berr[x]=0xff; else if (berr[x]<0) berr[x]=0;
220 r = rtable[rerr[x]];
221 g = gtable[gerr[x]];
222 b = btable[berr[x]];
224 pixel = (r<<roffs) | (g<<goffs) | (b<<boffs);
225 XPutPixel(ximg->image, x, y, pixel);
226 /* calc error */
227 rer = rerr[x] - r*dr;
228 ger = gerr[x] - g*dg;
229 ber = berr[x] - b*db;
231 /* distribute error */
232 r = (rer*3)/8;
233 g = (ger*3)/8;
234 b = (ber*3)/8;
235 /* x+1, y */
236 rerr[x+1]+=r;
237 gerr[x+1]+=g;
238 berr[x+1]+=b;
239 /* x, y+1 */
240 nrerr[x]+=r;
241 ngerr[x]+=g;
242 nberr[x]+=b;
243 /* x+1, y+1 */
244 nrerr[x+1]+=rer-2*r;
245 ngerr[x+1]+=ger-2*g;
246 nberr[x+1]+=ber-2*b;
248 ofs+=image->width;
249 /* skip to next line */
250 terr = rerr;
251 rerr = nrerr;
252 nrerr = terr;
254 terr = gerr;
255 gerr = ngerr;
256 ngerr = terr;
258 terr = berr;
259 berr = nberr;
260 nberr = terr;
263 return ximg;
267 static RXImage*
268 image2PseudoColor(RContext *ctx, RImage *image)
270 RXImage *ximg;
271 register int x, y, r, g, b;
272 unsigned char *red, *grn, *blu;
273 unsigned long pixel;
274 const int cpc=ctx->attribs->colors_per_channel;
275 const unsigned short rmask = cpc-1; /* different sizes could be used */
276 const unsigned short gmask = rmask; /* for r,g,b */
277 const unsigned short bmask = rmask;
278 unsigned short *rtable, *gtable, *btable;
279 const int cpccpc = cpc*cpc;
280 unsigned char *data;
281 int ofs;
282 /*register unsigned char maxrgb = 0xff;*/
284 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
285 if (!ximg) {
286 return NULL;
289 red = image->data[0];
290 grn = image->data[1];
291 blu = image->data[2];
293 data = ximg->image->data;
295 /* Tables are same at the moment because rmask==gmask==bmask. */
296 rtable = computeTable(rmask);
297 gtable = computeTable(gmask);
298 btable = computeTable(bmask);
300 if (rtable==NULL || gtable==NULL || btable==NULL) {
301 RErrorCode = RERR_NOMEMORY;
302 RDestroyXImage(ctx, ximg);
303 return NULL;
306 if (ctx->attribs->render_mode == RM_MATCH) {
307 /* fake match */
308 #ifdef DEBUG
309 printf("pseudo color match with %d colors per channel\n", cpc);
310 #endif
311 for (y=0, ofs = 0; y<image->height; y++) {
312 for (x=0; x<image->width; x++, ofs++) {
313 /* reduce pixel */
314 r = rtable[red[ofs]];
315 g = gtable[grn[ofs]];
316 b = btable[blu[ofs]];
317 pixel = r*cpccpc + g*cpc + b;
318 /*data[ofs] = ctx->colors[pixel].pixel;*/
319 XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel);
322 } else {
323 /* dither */
324 short *rerr, *gerr, *berr;
325 short *nrerr, *ngerr, *nberr;
326 short *terr;
327 int rer, ger, ber;
328 const int dr=0xff/rmask;
329 const int dg=0xff/gmask;
330 const int db=0xff/bmask;
332 #ifdef DEBUG
333 printf("pseudo color dithering with %d colors per channel\n", cpc);
334 #endif
335 rerr = (short*)alloca((image->width+2)*sizeof(short));
336 gerr = (short*)alloca((image->width+2)*sizeof(short));
337 berr = (short*)alloca((image->width+2)*sizeof(short));
338 nrerr = (short*)alloca((image->width+2)*sizeof(short));
339 ngerr = (short*)alloca((image->width+2)*sizeof(short));
340 nberr = (short*)alloca((image->width+2)*sizeof(short));
341 if (!rerr || !gerr || !berr || !nrerr || !ngerr || !nberr) {
342 RErrorCode = RERR_NOMEMORY;
343 RDestroyXImage(ctx, ximg);
344 return NULL;
346 for (x=0; x<image->width; x++) {
347 rerr[x] = red[x];
348 gerr[x] = grn[x];
349 berr[x] = blu[x];
351 rerr[x] = gerr[x] = berr[x] = 0;
352 /* convert and dither the image to XImage */
353 for (y=0, ofs=0; y<image->height; y++) {
354 if (y<image->height-1) {
355 int x1;
356 for (x=0, x1=ofs+image->width; x<image->width; x++, x1++) {
357 nrerr[x] = red[x1];
358 ngerr[x] = grn[x1];
359 nberr[x] = blu[x1];
361 /* last column */
362 x1--;
363 nrerr[x] = red[x1];
364 ngerr[x] = grn[x1];
365 nberr[x] = blu[x1];
367 for (x=0; x<image->width; x++, ofs++) {
368 /* reduce pixel */
369 if (rerr[x]>0xff) rerr[x]=0xff; else if (rerr[x]<0) rerr[x]=0;
370 if (gerr[x]>0xff) gerr[x]=0xff; else if (gerr[x]<0) gerr[x]=0;
371 if (berr[x]>0xff) berr[x]=0xff; else if (berr[x]<0) berr[x]=0;
373 r = rtable[rerr[x]];
374 g = gtable[gerr[x]];
375 b = btable[berr[x]];
377 pixel = r*cpccpc + g*cpc + b;
378 /*data[ofs] = ctx->colors[pixel].pixel;*/
379 XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel);
381 /* calc error */
382 rer = rerr[x] - r*dr;
383 ger = gerr[x] - g*dg;
384 ber = berr[x] - b*db;
386 /* distribute error */
387 rerr[x+1]+=(rer*7)/16;
388 gerr[x+1]+=(ger*7)/16;
389 berr[x+1]+=(ber*7)/16;
391 nrerr[x]+=(rer*5)/16;
392 ngerr[x]+=(ger*5)/16;
393 nberr[x]+=(ber*5)/16;
395 if (x>0) {
396 nrerr[x-1]+=(rer*3)/16;
397 ngerr[x-1]+=(ger*3)/16;
398 nberr[x-1]+=(ber*3)/16;
401 nrerr[x+1]+=rer/16;
402 ngerr[x+1]+=ger/16;
403 nberr[x+1]+=ber/16;
404 #if 0
405 /* distribute error */
406 r = (rer*3)/8;
407 g = (ger*3)/8;
408 b = (ber*3)/8;
409 /* x+1, y */
410 rerr[x+1]+=r;
411 gerr[x+1]+=g;
412 berr[x+1]+=b;
413 /* x, y+1 */
414 nrerr[x]+=r;
415 ngerr[x]+=g;
416 nberr[x]+=b;
417 /* x+1, y+1 */
418 nrerr[x+1]+=rer-2*r;
419 ngerr[x+1]+=ger-2*g;
420 nberr[x+1]+=ber-2*b;
421 #endif
423 /* skip to next line */
424 terr = rerr;
425 rerr = nrerr;
426 nrerr = terr;
428 terr = gerr;
429 gerr = ngerr;
430 ngerr = terr;
432 terr = berr;
433 berr = nberr;
434 nberr = terr;
437 ximg->image->data = (char*)data;
439 return ximg;
443 static RXImage*
444 image2GrayScale(RContext *ctx, RImage *image)
446 RXImage *ximg;
447 register int x, y, g;
448 unsigned char *red, *grn, *blu;
449 const int cpc=ctx->attribs->colors_per_channel;
450 unsigned short gmask;
451 unsigned short *table;
452 unsigned char *data;
453 int ofs;
454 /*register unsigned char maxrgb = 0xff;*/
456 ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
457 if (!ximg) {
458 return NULL;
461 red = image->data[0];
462 grn = image->data[1];
463 blu = image->data[2];
465 data = ximg->image->data;
467 if (ctx->vclass == StaticGray)
468 gmask = (1<<ctx->depth) - 1; /* use all grays */
469 else
470 gmask = cpc*cpc*cpc-1;
472 table = computeTable(gmask);
474 if (table==NULL) {
475 RErrorCode = RERR_NOMEMORY;
476 RDestroyXImage(ctx, ximg);
477 return NULL;
480 if (ctx->attribs->render_mode == RM_MATCH) {
481 /* fake match */
482 #ifdef DEBUG
483 printf("grayscale match with %d colors per channel\n", cpc);
484 #endif
485 for (y=0, ofs = 0; y<image->height; y++) {
486 for (x=0; x<image->width; x++, ofs++) {
487 /* reduce pixel */
488 g = table[(red[ofs]*30+grn[ofs]*59+blu[ofs]*11)/100];
490 /*data[ofs] = ctx->colors[g].pixel;*/
491 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
494 } else {
495 /* dither */
496 short *gerr;
497 short *ngerr;
498 short *terr;
499 int ger;
500 const int dg=0xff/gmask;
502 #ifdef DEBUG
503 printf("grayscale dither with %d colors per channel\n", cpc);
504 #endif
505 gerr = (short*)alloca((image->width+2)*sizeof(short));
506 ngerr = (short*)alloca((image->width+2)*sizeof(short));
507 if (!gerr || !ngerr) {
508 RErrorCode = RERR_NOMEMORY;
509 RDestroyXImage(ctx, ximg);
510 return NULL;
512 for (x=0; x<image->width; x++) {
513 gerr[x] = (red[x]*30 + grn[x]*59 + blu[x]*11)/100;
515 gerr[x] = 0;
516 /* convert and dither the image to XImage */
517 for (y=0, ofs=0; y<image->height; y++) {
518 if (y<image->height-1) {
519 int x1;
520 for (x=0, x1=ofs+image->width; x<image->width; x++, x1++) {
521 ngerr[x] = (red[x1]*30 + grn[x1]*59 + blu[x1]*11)/100;
523 /* last column */
524 x1--;
525 ngerr[x] = (red[x1]*30 + grn[x1]*59 + blu[x1]*11)/100;
527 for (x=0; x<image->width; x++, ofs++) {
528 /* reduce pixel */
529 if (gerr[x]>0xff) gerr[x]=0xff; else if (gerr[x]<0) gerr[x]=0;
531 g = table[gerr[x]];
533 /*data[ofs] = ctx->colors[g].pixel;*/
534 XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
535 /* calc error */
536 ger = gerr[x] - g*dg;
538 /* distribute error */
539 g = (ger*3)/8;
540 /* x+1, y */
541 gerr[x+1]+=g;
542 /* x, y+1 */
543 ngerr[x]+=g;
544 /* x+1, y+1 */
545 ngerr[x+1]+=ger-2*g;
547 /* skip to next line */
548 terr = gerr;
549 gerr = ngerr;
550 ngerr = terr;
553 ximg->image->data = (char*)data;
555 return ximg;
559 static RXImage*
560 image2Bitmap(RContext *ctx, RImage *image, int threshold)
562 RXImage *ximg;
563 unsigned char *alpha;
564 int x, y;
566 ximg = RCreateXImage(ctx, 1, image->width, image->height);
567 if (!ximg) {
568 return NULL;
570 alpha = image->data[3];
572 for (y = 0; y < image->height; y++) {
573 for (x = 0; x < image->width; x++) {
574 XPutPixel(ximg->image, x, y, (*alpha <= threshold ? 0 : 1));
575 alpha++;
579 return ximg;
584 int
585 RConvertImage(RContext *context, RImage *image, Pixmap *pixmap)
587 RXImage *ximg=NULL;
588 #ifdef XSHM
589 Pixmap tmp;
590 #endif
592 assert(context!=NULL);
593 assert(image!=NULL);
594 assert(pixmap!=NULL);
596 /* clear error message */
597 if (context->vclass == TrueColor)
598 ximg = image2TrueColor(context, image);
599 else if (context->vclass == PseudoColor || context->vclass == StaticColor)
600 ximg = image2PseudoColor(context, image);
601 else if (context->vclass == GrayScale || context->vclass == StaticGray)
602 ximg = image2GrayScale(context, image);
604 if (!ximg) {
605 #ifdef C_ALLOCA
606 alloca(0);
607 #endif
608 return False;
612 *pixmap = XCreatePixmap(context->dpy, context->drawable, image->width,
613 image->height, context->depth);
615 #ifdef XSHM
616 tmp = R_CreateXImageMappedPixmap(context, ximg);
617 if (tmp) {
619 * We have to copy the shm Pixmap into a normal Pixmap because
620 * otherwise, we would have to control when Pixmaps are freed so
621 * that we can detach their shm segments. This is a problem if the
622 * program crash, leaving stale shared memory segments in the
623 * system (lots of them). But with some work, we can optimize
624 * things and remove this XCopyArea. This will require
625 * explicitly freeing all pixmaps when exiting or restarting
626 * wmaker.
628 XCopyArea(context->dpy, tmp, *pixmap, context->copy_gc, 0, 0,
629 image->width, image->height, 0, 0);
630 XFreePixmap(context->dpy, tmp);
631 } else {
632 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0,
633 image->width, image->height);
635 #else /* !XSHM */
636 RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0,
637 image->width, image->height);
638 #endif /* !XSHM */
640 RDestroyXImage(context, ximg);
642 #ifdef C_ALLOCA
643 alloca(0);
644 #endif
645 return True;
649 int
650 RConvertImageMask(RContext *context, RImage *image, Pixmap *pixmap,
651 Pixmap *mask, int threshold)
653 GC gc;
654 XGCValues gcv;
655 RXImage *ximg=NULL;
657 assert(context!=NULL);
658 assert(image!=NULL);
659 assert(pixmap!=NULL);
660 assert(mask!=NULL);
662 if (!RConvertImage(context, image, pixmap))
663 return False;
665 if (image->data[3]==NULL) {
666 *mask = None;
667 return True;
670 ximg = image2Bitmap(context, image, threshold);
672 if (!ximg) {
673 #ifdef C_ALLOCA
674 alloca(0);
675 #endif
676 return False;
678 *mask = XCreatePixmap(context->dpy, context->drawable, image->width,
679 image->height, 1);
680 gcv.foreground = context->black;
681 gcv.background = context->white;
682 gcv.graphics_exposures = False;
683 gc = XCreateGC(context->dpy, *mask, GCForeground|GCBackground
684 |GCGraphicsExposures, &gcv);
685 RPutXImage(context, *mask, gc, ximg, 0, 0, 0, 0,
686 image->width, image->height);
687 RDestroyXImage(context, ximg);
689 #ifdef C_ALLOCA
690 alloca(0);
691 #endif
692 return True;
696 Bool
697 RGetClosestXColor(RContext *context, RColor *color, XColor *retColor)
699 if (context->vclass == TrueColor) {
700 unsigned short rmask, gmask, bmask;
701 unsigned short roffs, goffs, boffs;
702 unsigned short *rtable, *gtable, *btable;
704 roffs = context->red_offset;
705 goffs = context->green_offset;
706 boffs = context->blue_offset;
708 rmask = context->visual->red_mask >> roffs;
709 gmask = context->visual->green_mask >> goffs;
710 bmask = context->visual->blue_mask >> boffs;
712 rtable = computeTable(rmask);
713 gtable = computeTable(gmask);
714 btable = computeTable(bmask);
716 retColor->pixel = (rtable[color->red]<<roffs) |
717 (rtable[color->green]<<goffs) | (rtable[color->blue]<<boffs);
719 retColor->red = rtable[color->red] << 8;
720 retColor->green = rtable[color->green] << 8;
721 retColor->blue = rtable[color->blue] << 8;
722 retColor->flags = DoRed|DoGreen|DoBlue;
724 } else if (context->vclass == PseudoColor || context->vclass == StaticColor) {
725 const int cpc=context->attribs->colors_per_channel;
726 const unsigned short rmask = cpc-1; /* different sizes could be used */
727 const unsigned short gmask = rmask; /* for r,g,b */
728 const unsigned short bmask = rmask;
729 unsigned short *rtable, *gtable, *btable;
730 const int cpccpc = cpc*cpc;
731 int index;
733 rtable = computeTable(rmask);
734 gtable = computeTable(gmask);
735 btable = computeTable(bmask);
737 if (rtable==NULL || gtable==NULL || btable==NULL) {
738 RErrorCode = RERR_NOMEMORY;
739 return False;
741 index = rtable[color->red]*cpccpc + gtable[color->green]*cpc
742 + btable[color->blue];
743 *retColor = context->colors[index];
744 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
746 const int cpc = context->attribs->colors_per_channel;
747 unsigned short gmask;
748 unsigned short *table;
749 int index;
751 if (context->vclass == StaticGray)
752 gmask = (1<<context->depth) - 1; /* use all grays */
753 else
754 gmask = cpc*cpc*cpc-1;
756 table = computeTable(gmask);
757 if (!table)
758 return False;
760 index = table[(color->red*30 + color->green*59 + color->blue*11)/100];
762 *retColor = context->colors[index];
763 } else {
764 RErrorCode = RERR_INTERNAL;
765 return False;
768 return True;