mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-18 03:50:30 +01:00
This patch is just adding a single header, but because it also modifies all the C files to add the #include, it was made as a patch on its own to ease review. Signed-off-by: Christophe CURIS <christophe.curis@free.fr>
1061 lines
26 KiB
C
1061 lines
26 KiB
C
/* convert.c - convert RImage to Pixmap
|
|
*
|
|
* Raster graphics library
|
|
*
|
|
* Copyright (c) 1997-2003 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., 51 Franklin St, Fifth Floor, Boston,
|
|
* MA 02110-1301, USA.
|
|
*/
|
|
|
|
/* Problems:
|
|
* 1. Using Grayscale visual with Dithering crashes wmaker
|
|
* 2. Ghost dock/appicon is wrong in Pseudocolor, Staticgray, Grayscale
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "wraster.h"
|
|
#include "convert.h"
|
|
#include "xutil.h"
|
|
#include "wr_i18n.h"
|
|
|
|
|
|
#define NFREE(n) if (n) free(n)
|
|
|
|
#define HAS_ALPHA(I) ((I)->format == RRGBAFormat)
|
|
|
|
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 void release_conversion_table(void)
|
|
{
|
|
RConversionTable *tmp = conversionTable;
|
|
|
|
while (tmp) {
|
|
RConversionTable *tmp_to_delete = tmp;
|
|
|
|
tmp = tmp->next;
|
|
free(tmp_to_delete);
|
|
}
|
|
conversionTable = NULL;
|
|
}
|
|
|
|
static void release_std_conversion_table(void)
|
|
{
|
|
RStdConversionTable *tmp = stdConversionTable;
|
|
|
|
while (tmp) {
|
|
RStdConversionTable *tmp_to_delete = tmp;
|
|
|
|
tmp = tmp->next;
|
|
free(tmp_to_delete);
|
|
}
|
|
stdConversionTable = NULL;
|
|
}
|
|
|
|
void r_destroy_conversion_tables(void)
|
|
{
|
|
release_conversion_table();
|
|
release_std_conversion_table();
|
|
}
|
|
|
|
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,
|
|
signed char *err, signed char *nerr,
|
|
const unsigned short *rtable,
|
|
const unsigned short *gtable,
|
|
const unsigned short *btable,
|
|
const int dr, const int dg, const int db,
|
|
const unsigned short roffs, const unsigned short goffs, const unsigned short boffs)
|
|
{
|
|
signed char *terr;
|
|
int x, y, r, g, b;
|
|
int pixel;
|
|
int rer, ger, ber;
|
|
unsigned char *ptr = image->data;
|
|
int channels = (HAS_ALPHA(image) ? 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;
|
|
}
|
|
|
|
/* redither the 1st line to distribute error better */
|
|
ptr = image->data;
|
|
y = 0;
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 = (HAS_ALPHA(image) ? 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;
|
|
}
|
|
|
|
if (ctx->attribs->render_mode == RBestMatchRendering) {
|
|
int ofs;
|
|
unsigned long r, g, b;
|
|
int x, y;
|
|
unsigned long pixel;
|
|
unsigned char *ptr = image->data;
|
|
|
|
/* fake match */
|
|
#ifdef WRLIB_DEBUG
|
|
fputs("true color match\n", stderr);
|
|
#endif
|
|
if (rmask == 0xff && gmask == 0xff && bmask == 0xff) {
|
|
for (y = 0; y < image->height; y++) {
|
|
for (x = 0; x < image->width; x++, ptr += channels) {
|
|
/* reduce pixel */
|
|
r = ptr[0];
|
|
g = ptr[1];
|
|
b = ptr[2];
|
|
pixel = (r << roffs) | (g << goffs) | (b << boffs);
|
|
XPutPixel(ximg->image, x, y, pixel);
|
|
}
|
|
}
|
|
} else {
|
|
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 WRLIB_DEBUG
|
|
fputs("true color dither\n", stderr);
|
|
#endif
|
|
|
|
{
|
|
signed char *err;
|
|
signed char *nerr;
|
|
int ch = (HAS_ALPHA(image) ? 4 : 3);
|
|
|
|
err = malloc(ch * (image->width + 2));
|
|
nerr = malloc(ch * (image->width + 2));
|
|
if (!err || !nerr) {
|
|
NFREE(err);
|
|
NFREE(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);
|
|
}
|
|
|
|
}
|
|
|
|
return ximg;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
static void
|
|
convertPseudoColor_to_8(RXImage * ximg, RImage * image,
|
|
signed char *err, signed char *nerr,
|
|
const unsigned short *rtable,
|
|
const unsigned short *gtable,
|
|
const unsigned short *btable,
|
|
const int dr, const int dg, const int db, unsigned long *pixels, int cpc)
|
|
{
|
|
signed char *terr;
|
|
int x, y, r, g, b;
|
|
int pixel;
|
|
int rer, ger, ber;
|
|
unsigned char *ptr = image->data;
|
|
unsigned char *optr = (unsigned char *)ximg->image->data;
|
|
int channels = (HAS_ALPHA(image) ? 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 = (HAS_ALPHA(image) ? 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 WRLIB_DEBUG
|
|
fprintf(stderr, "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 - 3) {
|
|
/* 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 */
|
|
signed char *err;
|
|
signed char *nerr;
|
|
const int dr = 0xff / rmask;
|
|
const int dg = 0xff / gmask;
|
|
const int db = 0xff / bmask;
|
|
|
|
#ifdef WRLIB_DEBUG
|
|
fprintf(stderr, "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) {
|
|
NFREE(err);
|
|
NFREE(nerr);
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
memset(err, 0, 4 * (image->width + 3));
|
|
memset(nerr, 0, 4 * (image->width + 3));
|
|
|
|
convertPseudoColor_to_8(ximg, image, err + 4, nerr + 4,
|
|
rtable, gtable, btable, dr, dg, db, ctx->pixels, cpc);
|
|
|
|
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 = (HAS_ALPHA(image) ? 4 : 3);
|
|
|
|
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) {
|
|
/* reduce pixel */
|
|
pixel = (rtable[*ptr] + gtable[*(ptr + 1)]
|
|
+ btable[*(ptr + 2)] + base_pixel) & 0xffffffff;
|
|
|
|
XPutPixel(ximg->image, x, y, pixel);
|
|
}
|
|
}
|
|
} else {
|
|
/* dither */
|
|
signed short *err, *nerr;
|
|
signed short *terr;
|
|
int rer, ger, ber;
|
|
int x1, ofs;
|
|
|
|
#ifdef WRLIB_DEBUG
|
|
fprintf(stderr, "pseudo color dithering with %d colors per channel\n",
|
|
ctx->attribs->colors_per_channel);
|
|
#endif
|
|
err = (short *)malloc(3 * (image->width + 2) * sizeof(short));
|
|
nerr = (short *)malloc(3 * (image->width + 2) * sizeof(short));
|
|
if (!err || !nerr) {
|
|
NFREE(err);
|
|
NFREE(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 + 1] = err[x + 2] = 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 = (HAS_ALPHA(image) ? 4 : 3);
|
|
|
|
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 WRLIB_DEBUG
|
|
fprintf(stderr, "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 + 1) * 59 + *(ptr + 2) * 11) / 100];
|
|
ptr += channels;
|
|
/*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 WRLIB_DEBUG
|
|
fprintf(stderr, "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) {
|
|
NFREE(gerr);
|
|
NFREE(ngerr);
|
|
RErrorCode = RERR_NOMEMORY;
|
|
RDestroyXImage(ctx, ximg);
|
|
return NULL;
|
|
}
|
|
for (x = 0, y = 0; x < image->width; x++, y += channels) {
|
|
gerr[x] = (ptr[y] * 30 + ptr[y + 1] * 59 + ptr[y + 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 * channels; x < image->width;
|
|
x++, x1 += channels) {
|
|
ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 11) / 100;
|
|
}
|
|
/* last column */
|
|
x1 -= channels;
|
|
ngerr[x] = (ptr[x1] * 30 + ptr[x1 + 1] * 59 + ptr[x1 + 2] * 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 USE_XSHM
|
|
Pixmap tmp;
|
|
#endif
|
|
|
|
assert(context != NULL);
|
|
assert(image != NULL);
|
|
assert(pixmap != NULL);
|
|
|
|
switch (context->vclass) {
|
|
case TrueColor:
|
|
ximg = image2TrueColor(context, image);
|
|
break;
|
|
|
|
case PseudoColor:
|
|
case StaticColor:
|
|
if (context->attribs->standard_colormap_mode != RIgnoreStdColormap)
|
|
ximg = image2StandardPseudoColor(context, image);
|
|
else
|
|
ximg = image2PseudoColor(context, image);
|
|
break;
|
|
|
|
case GrayScale:
|
|
case StaticGray:
|
|
ximg = image2GrayScale(context, image);
|
|
break;
|
|
}
|
|
|
|
if (!ximg) {
|
|
return False;
|
|
}
|
|
|
|
*pixmap = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, context->depth);
|
|
|
|
#ifdef USE_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 /* !USE_XSHM */
|
|
RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
|
|
#endif /* !USE_XSHM */
|
|
|
|
RDestroyXImage(context, ximg);
|
|
|
|
return True;
|
|
}
|
|
|
|
/* make the gc permanent (create with context creation).
|
|
* GC creation is very expensive. altering its properties is not. -Dan
|
|
*/
|
|
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);
|
|
XFreeGC(context->dpy, gc);
|
|
|
|
return True;
|
|
}
|
|
|
|
Bool RGetClosestXColor(RContext * context, const 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 = (((unsigned long) rtable[color->red]) << roffs)
|
|
| (((unsigned long) gtable[color->green]) << goffs)
|
|
| (((unsigned long) 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;
|
|
}
|