mirror of
https://github.com/gnustep/libs-back.git
synced 2025-02-24 12:21:34 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/trunk@13271 72102866-910b-0410-8b05-ffd578937521
1065 lines
25 KiB
C
1065 lines
25 KiB
C
/* convert.c - convert RImage to Pixmap
|
|
*
|
|
* Raster graphics library
|
|
*
|
|
* Copyright (c) 1997-2000 Alfredo K. Kojima
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
#include <config.h>
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#ifdef BENCH
|
|
#include "bench.h"
|
|
#endif
|
|
|
|
#include "x11/wraster.h"
|
|
|
|
#ifdef XSHM
|
|
extern Pixmap R_CreateXImageMappedPixmap(RContext *context, RXImage *ximage);
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef ASM_X86
|
|
extern void x86_PseudoColor_32_to_8(unsigned char *image,
|
|
unsigned char *ximage,
|
|
char *err, char *nerr,
|
|
short *ctable,
|
|
int dr, int dg, int db,
|
|
unsigned long *pixels,
|
|
int cpc,
|
|
int width, int height,
|
|
int bytesPerPixel,
|
|
int line_offset);
|
|
#endif /* ASM_X86 */
|
|
|
|
#ifdef ASM_X86_MMX
|
|
|
|
extern int x86_check_mmx();
|
|
|
|
extern void x86_mmx_TrueColor_32_to_16(unsigned char *image,
|
|
unsigned short *ximage,
|
|
short *err, short *nerr,
|
|
short *rtable, short *gtable,
|
|
short *btable,
|
|
int dr, int dg, int db,
|
|
unsigned int roffs,
|
|
unsigned int goffs,
|
|
unsigned int boffs,
|
|
int width, int height,
|
|
int line_offset);
|
|
|
|
|
|
|
|
#endif /* ASM_X86_MMX */
|
|
|
|
|
|
|
|
typedef struct RConversionTable {
|
|
unsigned short table[256];
|
|
unsigned short index;
|
|
|
|
struct RConversionTable *next;
|
|
} RConversionTable;
|
|
|
|
|
|
typedef struct RStdConversionTable {
|
|
unsigned int table[256];
|
|
|
|
unsigned short mult;
|
|
unsigned short max;
|
|
|
|
struct RStdConversionTable *next;
|
|
} RStdConversionTable;
|
|
|
|
|
|
|
|
static RConversionTable *conversionTable = NULL;
|
|
static RStdConversionTable *stdConversionTable = NULL;
|
|
|
|
|
|
static unsigned short*
|
|
computeTable(unsigned short mask)
|
|
{
|
|
RConversionTable *tmp = conversionTable;
|
|
int i;
|
|
|
|
while (tmp) {
|
|
if (tmp->index == mask)
|
|
break;
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
if (tmp)
|
|
return tmp->table;
|
|
|
|
tmp = (RConversionTable *)malloc(sizeof(RConversionTable));
|
|
if (tmp == NULL)
|
|
return NULL;
|
|
|
|
for (i=0;i<256;i++)
|
|
tmp->table[i] = (i*mask + 0x7f)/0xff;
|
|
|
|
tmp->index = mask;
|
|
tmp->next = conversionTable;
|
|
conversionTable = tmp;
|
|
return tmp->table;
|
|
}
|
|
|
|
|
|
static unsigned int*
|
|
computeStdTable(unsigned int mult, unsigned int max)
|
|
{
|
|
RStdConversionTable *tmp = stdConversionTable;
|
|
unsigned int i;
|
|
|
|
while (tmp) {
|
|
if (tmp->mult == mult && tmp->max == max)
|
|
break;
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
if (tmp)
|
|
return tmp->table;
|
|
|
|
tmp = (RStdConversionTable *)malloc(sizeof(RStdConversionTable));
|
|
if (tmp == NULL)
|
|
return NULL;
|
|
|
|
for (i=0; i<256; i++) {
|
|
tmp->table[i] = (i*max)/0xff * mult;
|
|
}
|
|
tmp->mult = mult;
|
|
tmp->max = max;
|
|
|
|
tmp->next = stdConversionTable;
|
|
stdConversionTable = tmp;
|
|
|
|
return tmp->table;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
|
|
static void
|
|
convertTrueColor_generic(RXImage *ximg, RImage *image,
|
|
char *err, char *nerr,
|
|
const short *rtable,
|
|
const short *gtable,
|
|
const short *btable,
|
|
const int dr, const int dg, const int db,
|
|
const unsigned short roffs,
|
|
const unsigned short goffs,
|
|
const unsigned short boffs)
|
|
{
|
|
char *terr;
|
|
int x, y, r, g, b;
|
|
int pixel;
|
|
int rer, ger, ber;
|
|
unsigned char *ptr = image->data;
|
|
int channels = image->format == RRGBAFormat ? 4 : 3;
|
|
|
|
/* convert and dither the image to XImage */
|
|
for (y=0; y<image->height; y++) {
|
|
nerr[0] = 0;
|
|
nerr[1] = 0;
|
|
nerr[2] = 0;
|
|
for (x=0; x<image->width; x++, ptr+=channels) {
|
|
|
|
/* reduce pixel */
|
|
pixel = *ptr + err[x];
|
|
if (pixel<0) pixel=0; else if (pixel>0xff) pixel=0xff;
|
|
r = rtable[pixel];
|
|
/* calc error */
|
|
rer = pixel - r*dr;
|
|
|
|
/* reduce pixel */
|
|
pixel = *(ptr+1) + err[x+1];
|
|
if (pixel<0) pixel=0; else if (pixel>0xff) pixel=0xff;
|
|
g = gtable[pixel];
|
|
/* calc error */
|
|
ger = pixel - g*dg;
|
|
|
|
/* reduce pixel */
|
|
pixel = *(ptr+2) + err[x+2];
|
|
if (pixel<0) pixel=0; else if (pixel>0xff) pixel=0xff;
|
|
b = btable[pixel];
|
|
/* calc error */
|
|
ber = pixel - b*db;
|
|
|
|
|
|
pixel = (r<<roffs) | (g<<goffs) | (b<<boffs);
|
|
XPutPixel(ximg->image, x, y, pixel);
|
|
|
|
/* distribute error */
|
|
r = (rer*3)/8;
|
|
g = (ger*3)/8;
|
|
b = (ber*3)/8;
|
|
/* x+1, y */
|
|
err[x+3*1]+=r;
|
|
err[x+1+3*1]+=g;
|
|
err[x+2+3*1]+=b;
|
|
/* x, y+1 */
|
|
nerr[x]+=r;
|
|
nerr[x+1]+=g;
|
|
nerr[x+2]+=b;
|
|
/* x+1, y+1 */
|
|
nerr[x+3*1]=rer-2*r;
|
|
nerr[x+1+3*1]=ger-2*g;
|
|
nerr[x+2+3*1]=ber-2*b;
|
|
}
|
|
/* skip to next line */
|
|
terr = err;
|
|
err = nerr;
|
|
nerr = terr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
static RXImage*
|
|
image2TrueColor(RContext *ctx, RImage *image)
|
|
{
|
|
RXImage *ximg;
|
|
unsigned short rmask, gmask, bmask;
|
|
unsigned short roffs, goffs, boffs;
|
|
unsigned short *rtable, *gtable, *btable;
|
|
int channels = (image->format == RRGBAFormat ? 4 : 3);
|
|
|
|
ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
|
|
if (!ximg) {
|
|
return NULL;
|
|
}
|
|
|
|
roffs = ctx->red_offset;
|
|
goffs = ctx->green_offset;
|
|
boffs = ctx->blue_offset;
|
|
|
|
rmask = ctx->visual->red_mask >> roffs;
|
|
gmask = ctx->visual->green_mask >> goffs;
|
|
bmask = ctx->visual->blue_mask >> boffs;
|
|
|
|
rtable = computeTable(rmask);
|
|
gtable = computeTable(gmask);
|
|
btable = computeTable(bmask);
|
|
|
|
if (rtable==NULL || gtable==NULL || btable==NULL) {
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#ifdef BENCH
|
|
cycle_bench(1);
|
|
#endif
|
|
|
|
if (ctx->attribs->render_mode==RBestMatchRendering) {
|
|
int ofs, r, g, b;
|
|
int x, y;
|
|
unsigned long pixel;
|
|
unsigned char *ptr = image->data;
|
|
|
|
/* fake match */
|
|
#ifdef DEBUG
|
|
puts("true color match");
|
|
#endif
|
|
for (y=0, ofs=0; y < image->height; y++) {
|
|
for (x=0; x < image->width; x++, ofs+=channels-3) {
|
|
/* reduce pixel */
|
|
r = rtable[ptr[ofs++]];
|
|
g = gtable[ptr[ofs++]];
|
|
b = btable[ptr[ofs++]];
|
|
pixel = (r<<roffs) | (g<<goffs) | (b<<boffs);
|
|
XPutPixel(ximg->image, x, y, pixel);
|
|
}
|
|
}
|
|
} else {
|
|
/* dither */
|
|
const int dr=0xff/rmask;
|
|
const int dg=0xff/gmask;
|
|
const int db=0xff/bmask;
|
|
|
|
#ifdef DEBUG
|
|
puts("true color dither");
|
|
#endif
|
|
|
|
#ifdef ASM_X86_MMX
|
|
if (ctx->depth == 16 && image->format == RRGBAFormat
|
|
&& x86_check_mmx()) {
|
|
short *err;
|
|
short *nerr;
|
|
|
|
err = malloc(8*(image->width+3));
|
|
nerr = malloc(8*(image->width+3));
|
|
if (!err || !nerr) {
|
|
if (nerr)
|
|
free(nerr);
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
memset(err, 0, 8*(image->width+3));
|
|
memset(nerr, 0, 8*(image->width+3));
|
|
|
|
x86_mmx_TrueColor_32_to_16(image->data,
|
|
(unsigned short*)ximg->image->data,
|
|
err+8, nerr+8,
|
|
rtable, gtable, btable,
|
|
dr, dg, db,
|
|
roffs, goffs, boffs,
|
|
image->width, image->height,
|
|
ximg->image->bytes_per_line - 2*image->width);
|
|
|
|
free(err);
|
|
free(nerr);
|
|
} else
|
|
#endif /* ASM_X86_MMX */
|
|
{
|
|
char *err;
|
|
char *nerr;
|
|
int ch = image->format == RRGBAFormat ? 4 : 3;
|
|
|
|
err = malloc(ch*(image->width+2));
|
|
nerr = malloc(ch*(image->width+2));
|
|
if (!err || !nerr) {
|
|
if (nerr)
|
|
free(nerr);
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
|
|
memset(err, 0, ch*(image->width+2));
|
|
memset(nerr, 0, ch*(image->width+2));
|
|
|
|
convertTrueColor_generic(ximg, image, err, nerr,
|
|
rtable, gtable, btable,
|
|
dr, dg, db, roffs, goffs, boffs);
|
|
free(err);
|
|
free(nerr);
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef BENCH
|
|
cycle_bench(0);
|
|
#endif
|
|
|
|
return ximg;
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
static void
|
|
convertPseudoColor_to_8(RXImage *ximg, RImage *image,
|
|
char *err, char *nerr,
|
|
const short *rtable,
|
|
const short *gtable,
|
|
const short *btable,
|
|
const int dr, const int dg, const int db,
|
|
unsigned long *pixels,
|
|
int cpc)
|
|
{
|
|
char *terr;
|
|
int x, y, r, g, b;
|
|
int pixel;
|
|
int rer, ger, ber;
|
|
unsigned char *ptr = image->data;
|
|
unsigned char *optr = ximg->image->data;
|
|
int channels = image->format == RRGBAFormat ? 4 : 3;
|
|
int cpcpc = cpc*cpc;
|
|
|
|
/* convert and dither the image to XImage */
|
|
for (y=0; y<image->height; y++) {
|
|
nerr[0] = 0;
|
|
nerr[1] = 0;
|
|
nerr[2] = 0;
|
|
for (x=0; x<image->width*3; x+=3, ptr+=channels) {
|
|
|
|
/* reduce pixel */
|
|
pixel = *ptr + err[x];
|
|
if (pixel<0) pixel=0; else if (pixel>0xff) pixel=0xff;
|
|
r = rtable[pixel];
|
|
/* calc error */
|
|
rer = pixel - r*dr;
|
|
|
|
/* reduce pixel */
|
|
pixel = *(ptr+1) + err[x+1];
|
|
if (pixel<0) pixel=0; else if (pixel>0xff) pixel=0xff;
|
|
g = gtable[pixel];
|
|
/* calc error */
|
|
ger = pixel - g*dg;
|
|
|
|
/* reduce pixel */
|
|
pixel = *(ptr+2) + err[x+2];
|
|
if (pixel<0) pixel=0; else if (pixel>0xff) pixel=0xff;
|
|
b = btable[pixel];
|
|
/* calc error */
|
|
ber = pixel - b*db;
|
|
|
|
*optr++ = pixels[r*cpcpc + g*cpc + b];
|
|
|
|
/* distribute error */
|
|
r = (rer*3)/8;
|
|
g = (ger*3)/8;
|
|
b = (ber*3)/8;
|
|
|
|
/* x+1, y */
|
|
err[x+3*1]+=r;
|
|
err[x+1+3*1]+=g;
|
|
err[x+2+3*1]+=b;
|
|
/* x, y+1 */
|
|
nerr[x]+=r;
|
|
nerr[x+1]+=g;
|
|
nerr[x+2]+=b;
|
|
/* x+1, y+1 */
|
|
nerr[x+3*1]=rer-2*r;
|
|
nerr[x+1+3*1]=ger-2*g;
|
|
nerr[x+2+3*1]=ber-2*b;
|
|
}
|
|
/* skip to next line */
|
|
terr = err;
|
|
err = nerr;
|
|
nerr = terr;
|
|
|
|
optr += ximg->image->bytes_per_line - image->width;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static RXImage*
|
|
image2PseudoColor(RContext *ctx, RImage *image)
|
|
{
|
|
RXImage *ximg;
|
|
register int x, y, r, g, b;
|
|
unsigned char *ptr;
|
|
unsigned long pixel;
|
|
const int cpc=ctx->attribs->colors_per_channel;
|
|
const unsigned short rmask = cpc-1; /* different sizes could be used */
|
|
const unsigned short gmask = rmask; /* for r,g,b */
|
|
const unsigned short bmask = rmask;
|
|
unsigned short *rtable, *gtable, *btable;
|
|
const int cpccpc = cpc*cpc;
|
|
int channels = image->format == RRGBAFormat ? 4 : 3;
|
|
|
|
ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
|
|
if (!ximg) {
|
|
return NULL;
|
|
}
|
|
|
|
ptr = image->data;
|
|
|
|
/* Tables are same at the moment because rmask==gmask==bmask. */
|
|
rtable = computeTable(rmask);
|
|
gtable = computeTable(gmask);
|
|
btable = computeTable(bmask);
|
|
|
|
if (rtable==NULL || gtable==NULL || btable==NULL) {
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
|
|
if (ctx->attribs->render_mode == RBestMatchRendering) {
|
|
/* fake match */
|
|
#ifdef DEBUG
|
|
printf("pseudo color match with %d colors per channel\n", cpc);
|
|
#endif
|
|
for (y=0; y<image->height; y++) {
|
|
for (x=0; x<image->width; x++, ptr+=channels-1) {
|
|
/* reduce pixel */
|
|
r = rtable[*ptr++];
|
|
g = gtable[*ptr++];
|
|
b = btable[*ptr++];
|
|
pixel = r*cpccpc + g*cpc + b;
|
|
/*data[ofs] = ctx->colors[pixel].pixel;*/
|
|
XPutPixel(ximg->image, x, y, ctx->colors[pixel].pixel);
|
|
}
|
|
}
|
|
} else {
|
|
/* dither */
|
|
char *err;
|
|
char *nerr;
|
|
const int dr=0xff/rmask;
|
|
const int dg=0xff/gmask;
|
|
const int db=0xff/bmask;
|
|
|
|
|
|
#ifdef DEBUG
|
|
printf("pseudo color dithering with %d colors per channel\n", cpc);
|
|
#endif
|
|
err = malloc(4*(image->width+3));
|
|
nerr = malloc(4*(image->width+3));
|
|
if (!err || !nerr) {
|
|
if (nerr)
|
|
free(nerr);
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
memset(err, 0, 4*(image->width+3));
|
|
memset(nerr, 0, 4*(image->width+3));
|
|
|
|
#ifdef ASM_X86
|
|
x86_PseudoColor_32_to_8(image->data, ximg->image->data,
|
|
err+4, nerr+4,
|
|
rtable,
|
|
dr, dg, db, ctx->pixels, cpc,
|
|
image->width, image->height,
|
|
channels,
|
|
ximg->image->bytes_per_line - image->width);
|
|
#else
|
|
convertPseudoColor_to_8(ximg, image, err+4, nerr+4,
|
|
rtable, gtable, btable,
|
|
dr, dg, db, ctx->pixels, cpc);
|
|
#endif
|
|
|
|
free(err);
|
|
free(nerr);
|
|
}
|
|
|
|
return ximg;
|
|
}
|
|
|
|
|
|
/*
|
|
* For standard colormap
|
|
*/
|
|
static RXImage*
|
|
image2StandardPseudoColor(RContext *ctx, RImage *image)
|
|
{
|
|
RXImage *ximg;
|
|
register int x, y, r, g, b;
|
|
unsigned char *ptr;
|
|
unsigned long pixel;
|
|
unsigned char *data;
|
|
unsigned int *rtable, *gtable, *btable;
|
|
unsigned int base_pixel = ctx->std_rgb_map->base_pixel;
|
|
int channels = image->format == RRGBAFormat ? 4 : 3;
|
|
/*register unsigned char maxrgb = 0xff;*/
|
|
|
|
ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
|
|
if (!ximg) {
|
|
return NULL;
|
|
}
|
|
|
|
ptr = image->data;
|
|
|
|
data = (unsigned char *)ximg->image->data;
|
|
|
|
|
|
rtable = computeStdTable(ctx->std_rgb_map->red_mult,
|
|
ctx->std_rgb_map->red_max);
|
|
|
|
gtable = computeStdTable(ctx->std_rgb_map->green_mult,
|
|
ctx->std_rgb_map->green_max);
|
|
|
|
btable = computeStdTable(ctx->std_rgb_map->blue_mult,
|
|
ctx->std_rgb_map->blue_max);
|
|
|
|
if (rtable==NULL || gtable==NULL || btable==NULL) {
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
if (ctx->attribs->render_mode == RBestMatchRendering) {
|
|
for (y=0; y<image->height; y++) {
|
|
for (x=0; x<image->width; x++, ptr+=channels-3) {
|
|
/* reduce pixel */
|
|
pixel = (rtable[*ptr++] + gtable[*ptr++]
|
|
+ btable[*ptr++] + base_pixel) & 0xffffffff;
|
|
|
|
XPutPixel(ximg->image, x, y, pixel);
|
|
}
|
|
}
|
|
} else {
|
|
/* dither */
|
|
short *err, *nerr;
|
|
short *terr;
|
|
int rer, ger, ber;
|
|
int x1, ofs;
|
|
|
|
err = (short*)malloc(3*(image->width+2)*sizeof(short));
|
|
nerr = (short*)malloc(3*(image->width+2)*sizeof(short));
|
|
if (!err || !nerr) {
|
|
if (nerr)
|
|
free(nerr);
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
for (x=0, x1=0; x<image->width*3; x1+=channels-3) {
|
|
err[x++] = ptr[x1++];
|
|
err[x++] = ptr[x1++];
|
|
err[x++] = ptr[x1++];
|
|
}
|
|
err[x++] = err[x++] = err[x++] = 0;
|
|
/* convert and dither the image to XImage */
|
|
for (y=0, ofs=0; y<image->height; y++) {
|
|
if (y<image->height-1) {
|
|
int x1;
|
|
for (x=0, x1=(y+1)*image->width*channels;
|
|
x<image->width*3;
|
|
x1+=channels-3) {
|
|
nerr[x++] = ptr[x1++];
|
|
nerr[x++] = ptr[x1++];
|
|
nerr[x++] = ptr[x1++];
|
|
}
|
|
/* last column */
|
|
x1-=channels;
|
|
nerr[x++] = ptr[x1++];
|
|
nerr[x++] = ptr[x1++];
|
|
nerr[x++] = ptr[x1++];
|
|
}
|
|
for (x=0; x<image->width*3; x+=3, ofs++) {
|
|
/* reduce pixel */
|
|
if (err[x]>0xff) err[x]=0xff; else if (err[x]<0) err[x]=0;
|
|
if (err[x+1]>0xff) err[x+1]=0xff; else if (err[x+1]<0) err[x+1]=0;
|
|
if (err[x+2]>0xff) err[x+2]=0xff; else if (err[x+2]<0) err[x+2]=0;
|
|
|
|
r = rtable[err[x]];
|
|
g = gtable[err[x+1]];
|
|
b = btable[err[x+2]];
|
|
|
|
pixel = r + g + b;
|
|
|
|
data[ofs] = base_pixel + pixel;
|
|
|
|
/* calc error */
|
|
rer = err[x] - (ctx->colors[pixel].red>>8);
|
|
ger = err[x+1] - (ctx->colors[pixel].green>>8);
|
|
ber = err[x+2] - (ctx->colors[pixel].blue>>8);
|
|
|
|
/* distribute error */
|
|
err[x+3*1]+=(rer*7)/16;
|
|
err[x+1+3*1]+=(ger*7)/16;
|
|
err[x+2+3*1]+=(ber*7)/16;
|
|
|
|
nerr[x]+=(rer*5)/16;
|
|
nerr[x+1]+=(ger*5)/16;
|
|
nerr[x+2]+=(ber*5)/16;
|
|
|
|
if (x>0) {
|
|
nerr[x-3*1]+=(rer*3)/16;
|
|
nerr[x-3*1+1]+=(ger*3)/16;
|
|
nerr[x-3*1+2]+=(ber*3)/16;
|
|
}
|
|
|
|
nerr[x+3*1]+=rer/16;
|
|
nerr[x+1+3*1]+=ger/16;
|
|
nerr[x+2+3*1]+=ber/16;
|
|
}
|
|
/* skip to next line */
|
|
terr = err;
|
|
err = nerr;
|
|
nerr = terr;
|
|
|
|
ofs += ximg->image->bytes_per_line - image->width;
|
|
}
|
|
free(err);
|
|
free(nerr);
|
|
}
|
|
ximg->image->data = (char*)data;
|
|
|
|
return ximg;
|
|
}
|
|
|
|
|
|
|
|
static RXImage*
|
|
image2GrayScale(RContext *ctx, RImage *image)
|
|
{
|
|
RXImage *ximg;
|
|
register int x, y, g;
|
|
unsigned char *ptr;
|
|
const int cpc=ctx->attribs->colors_per_channel;
|
|
unsigned short gmask;
|
|
unsigned short *table;
|
|
unsigned char *data;
|
|
int channels = image->format == RRGBAFormat ? 4 : 3;
|
|
|
|
/*register unsigned char maxrgb = 0xff;*/
|
|
|
|
ximg = RCreateXImage(ctx, ctx->depth, image->width, image->height);
|
|
if (!ximg) {
|
|
return NULL;
|
|
}
|
|
|
|
ptr = image->data;
|
|
|
|
data = (unsigned char *)ximg->image->data;
|
|
|
|
if (ctx->vclass == StaticGray)
|
|
gmask = (1<<ctx->depth) - 1; /* use all grays */
|
|
else
|
|
gmask = cpc*cpc*cpc-1;
|
|
|
|
table = computeTable(gmask);
|
|
|
|
if (table==NULL) {
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
|
|
if (ctx->attribs->render_mode == RBestMatchRendering) {
|
|
/* fake match */
|
|
#ifdef DEBUG
|
|
printf("grayscale match with %d colors per channel\n", cpc);
|
|
#endif
|
|
for (y=0; y<image->height; y++) {
|
|
for (x=0; x<image->width; x++) {
|
|
/* reduce pixel */
|
|
g = table[(*ptr++ * 30 + *ptr++ * 59 + *ptr++ * 11)/100];
|
|
|
|
/*data[ofs] = ctx->colors[g].pixel;*/
|
|
XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
|
|
}
|
|
}
|
|
} else {
|
|
/* dither */
|
|
short *gerr;
|
|
short *ngerr;
|
|
short *terr;
|
|
int ger;
|
|
const int dg=0xff/gmask;
|
|
|
|
#ifdef DEBUG
|
|
printf("grayscale dither with %d colors per channel\n", cpc);
|
|
#endif
|
|
gerr = (short*)malloc((image->width+2)*sizeof(short));
|
|
ngerr = (short*)malloc((image->width+2)*sizeof(short));
|
|
if (!gerr || !ngerr) {
|
|
if (ngerr)
|
|
free(ngerr);
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
for (x=0; x<image->width; x++) {
|
|
gerr[x] = (ptr[x*3]*30 + ptr[x*3+1]*59 + ptr[x*3+2]*11)/100;
|
|
}
|
|
gerr[x] = 0;
|
|
/* convert and dither the image to XImage */
|
|
for (y=0; y<image->height; y++) {
|
|
if (y<image->height-1) {
|
|
int x1;
|
|
for (x=0, x1=(y+1)*image->width*3;
|
|
x<image->width;
|
|
x1+=channels-3) {
|
|
ngerr[x] = (ptr[x1++]*30 + ptr[x1++]*59 + ptr[x1++]*11)/100;
|
|
}
|
|
/* last column */
|
|
x1-=channels;
|
|
ngerr[x] = (ptr[x1++]*30 + ptr[x1++]*59 + ptr[x1++]*11)/100;
|
|
}
|
|
for (x=0; x<image->width; x++) {
|
|
/* reduce pixel */
|
|
if (gerr[x]>0xff) gerr[x]=0xff; else if (gerr[x]<0) gerr[x]=0;
|
|
|
|
g = table[gerr[x]];
|
|
|
|
/*data[ofs] = ctx->colors[g].pixel;*/
|
|
XPutPixel(ximg->image, x, y, ctx->colors[g].pixel);
|
|
/* calc error */
|
|
ger = gerr[x] - g*dg;
|
|
|
|
/* distribute error */
|
|
g = (ger*3)/8;
|
|
/* x+1, y */
|
|
gerr[x+1]+=g;
|
|
/* x, y+1 */
|
|
ngerr[x]+=g;
|
|
/* x+1, y+1 */
|
|
ngerr[x+1]+=ger-2*g;
|
|
}
|
|
/* skip to next line */
|
|
terr = gerr;
|
|
gerr = ngerr;
|
|
ngerr = terr;
|
|
}
|
|
free(gerr);
|
|
free(ngerr);
|
|
}
|
|
ximg->image->data = (char*)data;
|
|
|
|
return ximg;
|
|
}
|
|
|
|
|
|
static RXImage*
|
|
image2Bitmap(RContext *ctx, RImage *image, int threshold)
|
|
{
|
|
RXImage *ximg;
|
|
unsigned char *alpha;
|
|
int x, y;
|
|
|
|
ximg = RCreateXImage(ctx, 1, image->width, image->height);
|
|
if (!ximg) {
|
|
return NULL;
|
|
}
|
|
alpha = image->data+3;
|
|
|
|
for (y = 0; y < image->height; y++) {
|
|
for (x = 0; x < image->width; x++) {
|
|
XPutPixel(ximg->image, x, y, (*alpha <= threshold ? 0 : 1));
|
|
alpha+=4;
|
|
}
|
|
}
|
|
|
|
return ximg;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
RConvertImage(RContext *context, RImage *image, Pixmap *pixmap)
|
|
{
|
|
RXImage *ximg=NULL;
|
|
#ifdef XSHM
|
|
Pixmap tmp;
|
|
#endif
|
|
|
|
assert(context!=NULL);
|
|
assert(image!=NULL);
|
|
assert(pixmap!=NULL);
|
|
|
|
/* clear error message */
|
|
if (context->vclass == TrueColor) {
|
|
|
|
ximg = image2TrueColor(context, image);
|
|
|
|
} else if (context->vclass == PseudoColor
|
|
|| context->vclass == StaticColor) {
|
|
|
|
#ifdef BENCH
|
|
cycle_bench(1);
|
|
#endif
|
|
if (context->attribs->standard_colormap_mode != RIgnoreStdColormap)
|
|
ximg = image2StandardPseudoColor(context, image);
|
|
else
|
|
ximg = image2PseudoColor(context, image);
|
|
#ifdef BENCH
|
|
cycle_bench(0);
|
|
#endif
|
|
} else if (context->vclass == GrayScale || context->vclass == StaticGray) {
|
|
|
|
ximg = image2GrayScale(context, image);
|
|
}
|
|
|
|
if (!ximg) {
|
|
return False;
|
|
}
|
|
|
|
|
|
*pixmap = XCreatePixmap(context->dpy, context->drawable, image->width,
|
|
image->height, context->depth);
|
|
|
|
#ifdef XSHM
|
|
if (context->flags.use_shared_pixmap && ximg->is_shared)
|
|
tmp = R_CreateXImageMappedPixmap(context, ximg);
|
|
else
|
|
tmp = None;
|
|
if (tmp) {
|
|
/*
|
|
* We have to copy the shm Pixmap into a normal Pixmap because
|
|
* otherwise, we would have to control when Pixmaps are freed so
|
|
* that we can detach their shm segments. This is a problem if the
|
|
* program crash, leaving stale shared memory segments in the
|
|
* system (lots of them). But with some work, we can optimize
|
|
* things and remove this XCopyArea. This will require
|
|
* explicitly freeing all pixmaps when exiting or restarting
|
|
* wmaker.
|
|
*/
|
|
XCopyArea(context->dpy, tmp, *pixmap, context->copy_gc, 0, 0,
|
|
image->width, image->height, 0, 0);
|
|
XFreePixmap(context->dpy, tmp);
|
|
} else {
|
|
RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0,
|
|
image->width, image->height);
|
|
}
|
|
#else /* !XSHM */
|
|
RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0,
|
|
image->width, image->height);
|
|
#endif /* !XSHM */
|
|
|
|
RDestroyXImage(context, ximg);
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
int
|
|
RConvertImageMask(RContext *context, RImage *image, Pixmap *pixmap,
|
|
Pixmap *mask, int threshold)
|
|
{
|
|
GC gc;
|
|
XGCValues gcv;
|
|
RXImage *ximg=NULL;
|
|
|
|
assert(context!=NULL);
|
|
assert(image!=NULL);
|
|
assert(pixmap!=NULL);
|
|
assert(mask!=NULL);
|
|
|
|
if (!RConvertImage(context, image, pixmap))
|
|
return False;
|
|
|
|
if (image->format==RRGBFormat) {
|
|
*mask = None;
|
|
return True;
|
|
}
|
|
|
|
ximg = image2Bitmap(context, image, threshold);
|
|
|
|
if (!ximg) {
|
|
return False;
|
|
}
|
|
*mask = XCreatePixmap(context->dpy, context->drawable, image->width,
|
|
image->height, 1);
|
|
gcv.foreground = context->black;
|
|
gcv.background = context->white;
|
|
gcv.graphics_exposures = False;
|
|
gc = XCreateGC(context->dpy, *mask, GCForeground|GCBackground
|
|
|GCGraphicsExposures, &gcv);
|
|
RPutXImage(context, *mask, gc, ximg, 0, 0, 0, 0,
|
|
image->width, image->height);
|
|
RDestroyXImage(context, ximg);
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
Bool
|
|
RGetClosestXColor(RContext *context, RColor *color, XColor *retColor)
|
|
{
|
|
if (context->vclass == TrueColor) {
|
|
unsigned short rmask, gmask, bmask;
|
|
unsigned short roffs, goffs, boffs;
|
|
unsigned short *rtable, *gtable, *btable;
|
|
|
|
roffs = context->red_offset;
|
|
goffs = context->green_offset;
|
|
boffs = context->blue_offset;
|
|
|
|
rmask = context->visual->red_mask >> roffs;
|
|
gmask = context->visual->green_mask >> goffs;
|
|
bmask = context->visual->blue_mask >> boffs;
|
|
|
|
rtable = computeTable(rmask);
|
|
gtable = computeTable(gmask);
|
|
btable = computeTable(bmask);
|
|
|
|
retColor->pixel = (rtable[color->red]<<roffs) |
|
|
(gtable[color->green]<<goffs) | (btable[color->blue]<<boffs);
|
|
|
|
retColor->red = color->red << 8;
|
|
retColor->green = color->green << 8;
|
|
retColor->blue = color->blue << 8;
|
|
retColor->flags = DoRed|DoGreen|DoBlue;
|
|
|
|
} else if (context->vclass == PseudoColor
|
|
|| context->vclass == StaticColor) {
|
|
|
|
if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
|
|
unsigned int *rtable, *gtable, *btable;
|
|
|
|
rtable = computeStdTable(context->std_rgb_map->red_mult,
|
|
context->std_rgb_map->red_max);
|
|
|
|
gtable = computeStdTable(context->std_rgb_map->green_mult,
|
|
context->std_rgb_map->green_max);
|
|
|
|
btable = computeStdTable(context->std_rgb_map->blue_mult,
|
|
context->std_rgb_map->blue_max);
|
|
|
|
if (rtable==NULL || gtable==NULL || btable==NULL) {
|
|
RErrorCode = RERR_NOMEMORY;
|
|
return False;
|
|
}
|
|
|
|
retColor->pixel = (rtable[color->red]
|
|
+ gtable[color->green]
|
|
+ btable[color->blue]
|
|
+ context->std_rgb_map->base_pixel) & 0xffffffff;
|
|
retColor->red = color->red<<8;
|
|
retColor->green = color->green<<8;
|
|
retColor->blue = color->blue<<8;
|
|
retColor->flags = DoRed|DoGreen|DoBlue;
|
|
|
|
} else {
|
|
const int cpc=context->attribs->colors_per_channel;
|
|
const unsigned short rmask = cpc-1; /* different sizes could be used */
|
|
const unsigned short gmask = rmask; /* for r,g,b */
|
|
const unsigned short bmask = rmask;
|
|
unsigned short *rtable, *gtable, *btable;
|
|
const int cpccpc = cpc*cpc;
|
|
int index;
|
|
|
|
rtable = computeTable(rmask);
|
|
gtable = computeTable(gmask);
|
|
btable = computeTable(bmask);
|
|
|
|
if (rtable==NULL || gtable==NULL || btable==NULL) {
|
|
RErrorCode = RERR_NOMEMORY;
|
|
return False;
|
|
}
|
|
index = rtable[color->red]*cpccpc + gtable[color->green]*cpc
|
|
+ btable[color->blue];
|
|
*retColor = context->colors[index];
|
|
}
|
|
|
|
} else if (context->vclass == GrayScale || context->vclass == StaticGray) {
|
|
|
|
const int cpc = context->attribs->colors_per_channel;
|
|
unsigned short gmask;
|
|
unsigned short *table;
|
|
int index;
|
|
|
|
if (context->vclass == StaticGray)
|
|
gmask = (1<<context->depth) - 1; /* use all grays */
|
|
else
|
|
gmask = cpc*cpc*cpc-1;
|
|
|
|
table = computeTable(gmask);
|
|
if (!table)
|
|
return False;
|
|
|
|
index = table[(color->red*30 + color->green*59 + color->blue*11)/100];
|
|
|
|
*retColor = context->colors[index];
|
|
} else {
|
|
RErrorCode = RERR_INTERNAL;
|
|
return False;
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|