1255 lines
32 KiB
C
1255 lines
32 KiB
C
/*
|
|
* image.c
|
|
*
|
|
* Copyright (c) 1995 Erik Corry ehcorry@inet.uni-c.dk
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include "port_before.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include <X11/StringDefs.h>
|
|
#include <X11/Intrinsic.h>
|
|
|
|
#include "port_after.h"
|
|
|
|
#include "colorcube.h"
|
|
#include "dispdither.h"
|
|
#include "xcolorcube.h"
|
|
#include "image_endian.h"
|
|
#include "imagep.h"
|
|
#include "image_format.h"
|
|
|
|
#include "Chimera.h"
|
|
#include "ChimeraGUI.h"
|
|
#include "ChimeraRender.h"
|
|
|
|
#define TCOUNT 4
|
|
|
|
typedef void (*lt_expansion_fn) _ArgProto((byte *, byte *,
|
|
int, Intensity *, Intensity *,
|
|
Intensity *, int,
|
|
Intensity, Intensity,
|
|
Intensity));
|
|
|
|
#define IMAGE_GIF 0
|
|
#define IMAGE_XBM 1
|
|
#define IMAGE_JPEG 2
|
|
#define IMAGE_PNG 3
|
|
#define IMAGE_PNM 5
|
|
#define IMAGE_UNKNOWN 6
|
|
|
|
static struct content_map
|
|
{
|
|
char *name;
|
|
int id;
|
|
} content_map[] =
|
|
{
|
|
{ "image/gif", IMAGE_GIF },
|
|
{ "image/xbm", IMAGE_XBM },
|
|
{ "image/pnm", IMAGE_PNM },
|
|
#ifdef HAVE_JPEG
|
|
{ "image/jpeg", IMAGE_JPEG },
|
|
#endif
|
|
#ifdef HAVE_PNG
|
|
{ "image/x-png", IMAGE_PNG },
|
|
{ "image/png", IMAGE_PNG },
|
|
#endif
|
|
{ "image/x-xbitmap", IMAGE_XBM },
|
|
{ "image/x-portable-anymap", IMAGE_PNM },
|
|
{ "image/x-portable-bitmap", IMAGE_PNM },
|
|
{ "image/x-portable-graymap", IMAGE_PNM },
|
|
{ "image/x-portable-pixmap", IMAGE_PNM },
|
|
{ NULL, IMAGE_UNKNOWN },
|
|
};
|
|
|
|
/*
|
|
* Holds the infomation about a particular display
|
|
*/
|
|
typedef struct imagestate
|
|
{
|
|
MemPool mp;
|
|
|
|
/* X information */
|
|
Display *dpy;
|
|
Colormap cmap;
|
|
int depth;
|
|
Visual *v;
|
|
Widget w;
|
|
Window win; /* draw window */
|
|
Pixel bg, fg; /* background, foreground for bitmaps */
|
|
XColor bgcolor;
|
|
|
|
XImage *xi;
|
|
int last_line;
|
|
int ref_count;
|
|
char *hash;
|
|
|
|
/* Generic Image decoding vars */
|
|
cct_dither_table dither_table[TCOUNT];/* Table to dither/convert to screen */
|
|
int free_dither_table; /* How many tables were allocated (0-4) */
|
|
ddt_dither_fn dither_function; /* Function to dither/convert with */
|
|
bool dithering;
|
|
|
|
struct ifs_vector if_vector; /* Image format decoder info */
|
|
|
|
byte *expansion_buf; /* Area for upgrading to bigger depth */
|
|
lt_expansion_fn expansion_function; /* Func for upgrading to bigger depth */
|
|
|
|
int expanded_type; /* Image type after expansion */
|
|
int image_special_count; /* colors allocated for this image */
|
|
int image_special_max; /* max colors to alloc for this image */
|
|
bool no_more_specials;
|
|
bool special_blacklist[256]; /* already tried to allocate pixel once */
|
|
bool special_real_blacklist[256]; /* really tried to allocate pixel once */
|
|
|
|
/* control stuff */
|
|
ChimeraSink wp;
|
|
ChimeraGUI wd;
|
|
struct imageclass *ic;
|
|
struct imagestate *prev, *next;
|
|
} ImageState;
|
|
|
|
/*
|
|
* Hold global information about the image module
|
|
*/
|
|
typedef struct imageclass
|
|
{
|
|
MemPool mp;
|
|
int icount;
|
|
bool init;
|
|
GC gc;
|
|
int refcount;
|
|
|
|
/* Image decoding stuff */
|
|
cct_cube colorcube; /* cuboid of allocated colors on screen */
|
|
cct_cube grayscale; /* gray shades allocated on screen */
|
|
cct_dither_table color_tables[TCOUNT]; /* color dither table */
|
|
cct_dither_table gray_tables[TCOUNT]; /* color dither table */
|
|
struct ccs_special_color special_colors[256]; /* specially alloc'd colors */
|
|
int special_count;
|
|
int special_max;
|
|
} ImageClass;
|
|
|
|
void SetSize _ArgProto((ImageState *));
|
|
static void ImageToXImage _ArgProto((void *, int, int));
|
|
static Image *lf_get_image _ArgProto((ImageState *));
|
|
static void lf_set_up_bitmap _ArgProto((ImageState *));
|
|
static void lf_set_up_expansion _ArgProto((ImageState *));
|
|
static byte *lf_get_8_bit_map _ArgProto((MemPool, Intensity *));
|
|
static bool lf_null_gray_map _ArgProto((byte **));
|
|
static void lf_set_up_dither _ArgProto((ImageState *));
|
|
static void lf_get_new_specials _ArgProto((ImageState *, ImageClass *,
|
|
Image *, byte *));
|
|
static void ImageClassInit _ArgProto((ImageClass *, Display *));
|
|
static void ImageAdd _ArgProto((void *));
|
|
static void ImageEnd _ArgProto((void *));
|
|
static void ImageDestroy _ArgProto((void *));
|
|
void *ImageInit _ArgProto((ChimeraRender, void *, void *));
|
|
static bool ImageExpose _ArgProto((void *, int, int,
|
|
unsigned int, unsigned int));
|
|
|
|
static byte *
|
|
lf_get_8_bit_map(mp, old_table)
|
|
MemPool mp;
|
|
Intensity *old_table;
|
|
{
|
|
byte *answer = (byte *)MPCGet(mp, sizeof(byte) * 256);
|
|
int i;
|
|
|
|
for (i = 0; i < 256; i++)
|
|
answer[i] = old_table[i] >> 8;
|
|
|
|
return answer;
|
|
}
|
|
|
|
static bool
|
|
lf_null_gray_map(maps)
|
|
byte *maps[3];
|
|
{
|
|
int i;
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
if (maps[0][i] != maps[1][i]) return false;
|
|
if (maps[2][i] != maps[1][i]) return false;
|
|
if (maps[2][i] != i) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static Image *
|
|
lf_get_image(is)
|
|
ImageState *is;
|
|
{
|
|
if (is->if_vector.getImageProc && is->if_vector.image_format_closure)
|
|
return(is->if_vector.getImageProc(is->if_vector.image_format_closure));
|
|
return(NULL);
|
|
}
|
|
|
|
static void
|
|
lf_set_up_bitmap(is)
|
|
ImageState *is;
|
|
{
|
|
Image *image = lf_get_image(is);
|
|
ImageClass *ic = is->ic;
|
|
bool really_allocated = false;
|
|
int i;
|
|
Intensity intensities[3];
|
|
|
|
is->expanded_type = image->type;
|
|
|
|
if(image->type == IBITMAP)
|
|
{
|
|
is->bg = is->bgcolor.pixel;
|
|
is->fg = BlackPixel(is->dpy, DefaultScreen(is->dpy));
|
|
return;
|
|
}
|
|
|
|
for(i = 0; i < 2; i++)
|
|
{
|
|
bool really_allocated_last_time = really_allocated;
|
|
if(image->transparent != i)
|
|
{
|
|
intensities[0] = image->rgb.red[i];
|
|
intensities[1] = image->rgb.green[i];
|
|
intensities[2] = image->rgb.blue[i];
|
|
if (!xccf_allocate_special(is->dpy,
|
|
is->cmap,
|
|
ic->colorcube,
|
|
ic->grayscale,
|
|
intensities,
|
|
&really_allocated,
|
|
/* don't-really-allocate-flag */
|
|
ic->special_count >= ic->special_max,
|
|
ic->special_colors,
|
|
ic->special_count,
|
|
i ? &(is->fg): &(is->bg)))
|
|
{
|
|
if(really_allocated_last_time)
|
|
{
|
|
XFreeColors(is->dpy,
|
|
is->cmap,
|
|
&(ic->special_colors[ic->special_count - 1].pixel),
|
|
1,
|
|
0);
|
|
ic->special_count--;
|
|
}
|
|
|
|
if(image->rgb.red[0] == image->rgb.green[0] &&
|
|
image->rgb.red[0] == image->rgb.blue[0] &&
|
|
image->rgb.red[1] == image->rgb.green[1] &&
|
|
image->rgb.red[1] == image->rgb.blue[1])
|
|
{
|
|
is->expanded_type = IGRAY;
|
|
is->expansion_buf = MPCGet(is->mp, image->width + 64);
|
|
}
|
|
else
|
|
{
|
|
is->expanded_type = ITRUE;
|
|
is->expansion_buf = MPCGet(is->mp, (3 * image->width) + 192);
|
|
}
|
|
|
|
return;
|
|
}
|
|
if(really_allocated) ic->special_count++;
|
|
}
|
|
else
|
|
{
|
|
if (i) is->fg = is->bgcolor.pixel;
|
|
else is->bg = is->bgcolor.pixel;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
lf_expand_bitmap_to_true(
|
|
byte *expansion_buffer,
|
|
byte *source_buffer,
|
|
int width,
|
|
Intensity *red_cmap,
|
|
Intensity *green_cmap,
|
|
Intensity *blue_cmap,
|
|
int transparent,
|
|
Intensity red_bg,
|
|
Intensity green_bg,
|
|
Intensity blue_bg)
|
|
{
|
|
int i;
|
|
byte red[2];
|
|
byte green[2];
|
|
byte blue[2];
|
|
red[0] = red_cmap[0] >> 8;
|
|
red[1] = red_cmap[1] >> 8;
|
|
green[0] = green_cmap[0] >> 8;
|
|
green[1] = green_cmap[1] >> 8;
|
|
blue[0] = blue_cmap[0] >> 8;
|
|
blue[1] = blue_cmap[1] >> 8;
|
|
if(transparent != -1)
|
|
{
|
|
red[transparent] = red_bg >> 8;
|
|
green[transparent] = green_bg >> 8;
|
|
blue[transparent] = blue_bg >> 8;
|
|
}
|
|
for(i = (width + CHAR_BITS - 1) / CHAR_BITS; i; i--)
|
|
{
|
|
byte t = *source_buffer++;
|
|
# ifdef CHIMERA_LITTLE_ENDIAN
|
|
expansion_buffer[0] = red[t & 1];
|
|
expansion_buffer[1] = green[t & 1];
|
|
expansion_buffer[2] = blue[t & 1];
|
|
expansion_buffer[3] = red[t >> 1 & 1];
|
|
expansion_buffer[4] = green[t >> 1 & 1];
|
|
expansion_buffer[5] = blue[t >> 1 & 1];
|
|
expansion_buffer[6] = red[t >> 2 & 1];
|
|
expansion_buffer[7] = green[t >> 2 & 1];
|
|
expansion_buffer[8] = blue[t >> 2 & 1];
|
|
expansion_buffer[9] = red[t >> 3 & 1];
|
|
expansion_buffer[10] = green[t >> 3 & 1];
|
|
expansion_buffer[11] = blue[t >> 3 & 1];
|
|
expansion_buffer[12] = red[t >> 4 & 1];
|
|
expansion_buffer[13] = green[t >> 4 & 1];
|
|
expansion_buffer[14] = blue[t >> 4 & 1];
|
|
expansion_buffer[15] = red[t >> 5 & 1];
|
|
expansion_buffer[16] = green[t >> 5 & 1];
|
|
expansion_buffer[17] = blue[t >> 5 & 1];
|
|
expansion_buffer[18] = red[t >> 6 & 1];
|
|
expansion_buffer[19] = green[t >> 6 & 1];
|
|
expansion_buffer[20] = blue[t >> 6 & 1];
|
|
expansion_buffer[21] = red[t >> 7 & 1];
|
|
expansion_buffer[22] = green[t >> 7 & 1];
|
|
expansion_buffer[23] = blue[t >> 7 & 1];
|
|
# else
|
|
expansion_buffer[0] = red[t >> 7 & 1];
|
|
expansion_buffer[1] = green[t >> 7 & 1];
|
|
expansion_buffer[2] = blue[t >> 7 & 1];
|
|
expansion_buffer[3] = red[t >> 6 & 1];
|
|
expansion_buffer[4] = green[t >> 6 & 1];
|
|
expansion_buffer[5] = blue[t >> 6 & 1];
|
|
expansion_buffer[6] = red[t >> 5 & 1];
|
|
expansion_buffer[7] = green[t >> 5 & 1];
|
|
expansion_buffer[8] = blue[t >> 5 & 1];
|
|
expansion_buffer[9] = red[t >> 4 & 1];
|
|
expansion_buffer[10] = green[t >> 4 & 1];
|
|
expansion_buffer[11] = blue[t >> 4 & 1];
|
|
expansion_buffer[12] = red[t >> 3 & 1];
|
|
expansion_buffer[13] = green[t >> 3 & 1];
|
|
expansion_buffer[14] = blue[t >> 3 & 1];
|
|
expansion_buffer[15] = red[t >> 2 & 1];
|
|
expansion_buffer[16] = green[t >> 2 & 1];
|
|
expansion_buffer[17] = blue[t >> 2 & 1];
|
|
expansion_buffer[18] = red[t >> 1 & 1];
|
|
expansion_buffer[19] = green[t >> 1 & 1];
|
|
expansion_buffer[20] = blue[t >> 1 & 1];
|
|
expansion_buffer[21] = red[t & 1];
|
|
expansion_buffer[22] = green[t & 1];
|
|
expansion_buffer[23] = blue[t & 1];
|
|
# endif
|
|
expansion_buffer += 24;
|
|
}
|
|
}
|
|
|
|
static void
|
|
lf_expand_bitmap_to_gray(
|
|
byte *expansion_buffer,
|
|
byte *source_buffer,
|
|
int width,
|
|
Intensity *red_cmap,
|
|
Intensity *green_cmap,
|
|
Intensity *blue_cmap,
|
|
int transparent,
|
|
Intensity red_bg,
|
|
Intensity green_bg,
|
|
Intensity blue_bg)
|
|
{
|
|
int i;
|
|
byte gray[2];
|
|
gray[0] = red_cmap[0] >> 8;
|
|
gray[1] = red_cmap[1] >> 8;
|
|
if(transparent != -1)
|
|
gray[transparent] = red_bg >> 8;
|
|
|
|
for(i = (width + CHAR_BITS - 1) / CHAR_BITS; i; i--)
|
|
{
|
|
byte t = *source_buffer++;
|
|
# ifdef CHIMERA_LITTLE_ENDIAN
|
|
expansion_buffer[0] = gray[t & 1];
|
|
expansion_buffer[1] = gray[t >> 1 & 1];
|
|
expansion_buffer[2] = gray[t >> 2 & 1];
|
|
expansion_buffer[3] = gray[t >> 3 & 1];
|
|
expansion_buffer[4] = gray[t >> 4 & 1];
|
|
expansion_buffer[5] = gray[t >> 5 & 1];
|
|
expansion_buffer[6] = gray[t >> 6 & 1];
|
|
expansion_buffer[7] = gray[t >> 7 & 1];
|
|
# else
|
|
expansion_buffer[0] = gray[t >> 7 & 1];
|
|
expansion_buffer[1] = gray[t >> 6 & 1];
|
|
expansion_buffer[2] = gray[t >> 5 & 1];
|
|
expansion_buffer[3] = gray[t >> 4 & 1];
|
|
expansion_buffer[4] = gray[t >> 3 & 1];
|
|
expansion_buffer[5] = gray[t >> 2 & 1];
|
|
expansion_buffer[6] = gray[t >> 1 & 1];
|
|
expansion_buffer[7] = gray[t & 1];
|
|
# endif
|
|
expansion_buffer += 8;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
lf_set_up_expansion(is)
|
|
ImageState *is;
|
|
{
|
|
if(is->expanded_type == IGRAY)
|
|
is->expansion_function = &lf_expand_bitmap_to_gray;
|
|
else
|
|
is->expansion_function = &lf_expand_bitmap_to_true;
|
|
}
|
|
|
|
static void
|
|
lf_set_up_dither(is)
|
|
ImageState *is;
|
|
{
|
|
byte *maps[3];
|
|
int i;
|
|
cct_8_true_conversion_table cdt;
|
|
ImageClass *ic = is->ic;
|
|
Image *image = lf_get_image(is);
|
|
int image_type;
|
|
|
|
is->dithering = false;
|
|
|
|
if(is->expansion_buf)
|
|
{
|
|
if(image->depth > 1) is->expanded_type = image_type = ITRUE;
|
|
else image_type = is->expanded_type; /* lf_set_up_bitmap already set it */
|
|
}
|
|
else
|
|
{
|
|
image_type = image->type;
|
|
}
|
|
|
|
maps[0] = maps[1] = maps[2] = 0;
|
|
|
|
if (image->type == IRGB || image->type == IGRAY)
|
|
{
|
|
maps[0] = lf_get_8_bit_map(is->mp, image->rgb.red);
|
|
maps[1] = lf_get_8_bit_map(is->mp, image->rgb.green);
|
|
maps[2] = lf_get_8_bit_map(is->mp, image->rgb.blue);
|
|
}
|
|
|
|
if (image->transparent != -1)
|
|
{
|
|
maps[0][image->transparent] = is->bgcolor.red >> 8;
|
|
maps[1][image->transparent] = is->bgcolor.green >> 8;
|
|
maps[2][image->transparent] = is->bgcolor.blue >> 8;
|
|
}
|
|
|
|
/*
|
|
* A real color dither/conversion?
|
|
*/
|
|
if ((image_type == IRGB || image_type == ITRUE) && ic->colorcube)
|
|
{
|
|
if (image_type == IRGB && ic->colorcube->cube_type == cube_true_color)
|
|
{
|
|
/*
|
|
* colormap to true-color conversion needed
|
|
*/
|
|
if (!ic->color_tables[0].true_true_conversion)
|
|
{
|
|
ic->color_tables[0].true_true_conversion =
|
|
ccf_create_true_true_conversion_table(ic->colorcube);
|
|
}
|
|
cdt = ccf_true_true_to_8_true_conversion_table(
|
|
ic->color_tables[0].
|
|
true_true_conversion,
|
|
image->rgb.used,
|
|
maps);
|
|
is->dither_table[0].eight_true_conversion = cdt;
|
|
is->dither_table[1].eight_true_conversion = cdt;
|
|
is->dither_table[2].eight_true_conversion = cdt;
|
|
is->dither_table[3].eight_true_conversion = cdt;
|
|
is->free_dither_table = 1;
|
|
is->dither_function = &ddf_convert_line_8;
|
|
is->dithering = false;
|
|
}
|
|
else if (image_type == IRGB &&
|
|
(ic->colorcube->cube_type == cube_mapping ||
|
|
ic->colorcube->cube_type == cube_no_mapping))
|
|
{
|
|
/*
|
|
* 8-bit palette to 8-bit screen dither
|
|
*/
|
|
if (!ic->color_tables[0].true_8_dither)
|
|
for (i = 0; i < TCOUNT; i++)
|
|
ic->color_tables[i].true_8_dither =
|
|
ccf_create_true_8_dither_table(i, 4, 4, ic->colorcube);
|
|
for (i = 0; i < TCOUNT; i++)
|
|
{
|
|
is->dither_table[i].eight_8_dither =
|
|
ccf_true_8_to_8_8_dither_table(ic->color_tables[i].true_8_dither,
|
|
image->rgb.used, maps);
|
|
if(image->transparent != -1)
|
|
ccf_set_specially_allocated(is->dither_table[i].eight_8_dither,
|
|
image->transparent,
|
|
is->bgcolor.pixel);
|
|
}
|
|
is->free_dither_table = TCOUNT;
|
|
is->dither_function = &ddf_dither_line_8;
|
|
is->dithering = true;
|
|
}
|
|
else if (image_type == ITRUE &&
|
|
ic->colorcube->cube_type == cube_true_color)
|
|
{
|
|
/*
|
|
* true-true conversion job
|
|
*/
|
|
if (!ic->color_tables[0].true_true_conversion)
|
|
ic->color_tables[0].true_true_conversion =
|
|
ccf_create_true_true_conversion_table(ic->colorcube);
|
|
for (i = 0; i < TCOUNT; i++)
|
|
is->dither_table[i].true_true_conversion =
|
|
ic->color_tables[0].true_true_conversion;
|
|
is->free_dither_table = 0; /* don't free it its a copied pointer!! */
|
|
is->dither_function = &ddf_convert_line_24;
|
|
is->dithering = false;
|
|
}
|
|
else if (image_type == ITRUE &&
|
|
(ic->colorcube->cube_type == cube_mapping ||
|
|
ic->colorcube->cube_type == cube_no_mapping))
|
|
{
|
|
/*
|
|
* true-color to 8-bit dither-type-situation
|
|
*/
|
|
if (!ic->color_tables[0].true_8_dither)
|
|
for (i = 0; i < TCOUNT; i++)
|
|
ic->color_tables[i].true_8_dither =
|
|
ccf_create_true_8_dither_table(i, 4, 4, ic->colorcube);
|
|
for (i = 0; i < TCOUNT; i++)
|
|
is->dither_table[i].true_8_dither =
|
|
ic->color_tables[i].true_8_dither;
|
|
is->free_dither_table = 0; /* don't free it its a copied pointer!! */
|
|
is->dither_function = &ddf_color_dither_line_24;
|
|
is->dithering = true;
|
|
}
|
|
}
|
|
/*
|
|
* else a black and white dither
|
|
*/
|
|
else if ((image_type == IRGB || image_type == ITRUE ||
|
|
image_type == IGRAY) &&
|
|
ic->grayscale && ic->grayscale->u.grayscale.value_count < 29)
|
|
{
|
|
if (!ic->gray_tables[0].eight_8_dither)
|
|
for (i = 0; i < TCOUNT; i++)
|
|
ic->gray_tables[i].eight_8_dither =
|
|
ccf_create_gray_dither_table(i, 4, 4, ic->grayscale);
|
|
if(is->depth == 1)
|
|
{
|
|
/* 1-bit images need special handling because they are not
|
|
displayed using the colormap in an XPutImage. Instead the
|
|
Graphics context foreground and background colors are used */
|
|
if(ic->grayscale->u.grayscale.pixel_values[1])
|
|
{
|
|
is->fg = ic->grayscale->u.grayscale.pixel_values[1];
|
|
is->bg = ic->grayscale->u.grayscale.pixel_values[0];
|
|
}
|
|
else
|
|
{
|
|
is->fg = ic->grayscale->u.grayscale.pixel_values[0];
|
|
is->bg = ic->grayscale->u.grayscale.pixel_values[1];
|
|
}
|
|
}
|
|
if (image_type == IRGB || image_type == IGRAY)
|
|
{
|
|
/*
|
|
* colormap to grayscale dither
|
|
*/
|
|
if (lf_null_gray_map(maps) && image->transparent == -1)
|
|
{
|
|
for (i = 0; i < TCOUNT; i++)
|
|
is->dither_table[i].eight_8_dither =
|
|
ic->gray_tables[i].eight_8_dither;
|
|
is->free_dither_table = 0; /* don't free it its a copied pointer */
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < TCOUNT; i++)
|
|
{
|
|
is->dither_table[i].eight_8_dither =
|
|
ccf_gray_to_gray_dither_convert(ic->gray_tables[i].
|
|
eight_8_dither,
|
|
image->rgb.used,
|
|
maps);
|
|
if(image->transparent != -1)
|
|
ccf_set_specially_allocated(is->dither_table[i].eight_8_dither,
|
|
image->transparent,
|
|
is->bgcolor.pixel);
|
|
|
|
}
|
|
is->free_dither_table = 4;
|
|
}
|
|
is->dither_function = &ddf_dither_line_8;
|
|
is->dithering = true;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* True-color-to-grayscale type situtation
|
|
*/
|
|
for (i = 0; i < TCOUNT; i++)
|
|
is->dither_table[i].true_8_dither =
|
|
ic->gray_tables[i].true_8_dither;
|
|
is->free_dither_table = 0; /* don't free it its a copied pointer!! */
|
|
is->dither_function = &ddf_gray_dither_line_24;
|
|
is->dithering = true;
|
|
}
|
|
}
|
|
/*
|
|
* else a bw conversion
|
|
*/
|
|
else if ((image_type == IRGB || image_type == ITRUE ||
|
|
image_type == IGRAY) &&
|
|
ic->grayscale &&
|
|
ic->grayscale->u.grayscale.value_count >= 29)
|
|
{
|
|
if (image->transparent != -1 &&
|
|
(image_type == IRGB || image_type == IGRAY))
|
|
{
|
|
maps[0][image->transparent] = is->bgcolor.red >> 8;
|
|
maps[1][image->transparent] = is->bgcolor.green >> 8;
|
|
maps[2][image->transparent] = is->bgcolor.blue >> 8;
|
|
}
|
|
|
|
if (!ic->gray_tables[0].eight_true_conversion)
|
|
ic->gray_tables[0].eight_true_conversion =
|
|
ccf_create_gray_conversion_table(ic->grayscale);
|
|
if (image_type == IGRAY || image_type == IRGB)
|
|
{
|
|
/*
|
|
* Simple remap of input values to output values
|
|
*/
|
|
if (lf_null_gray_map(maps))
|
|
{
|
|
for (i = 0; i < TCOUNT; i++)
|
|
is->dither_table[i].eight_true_conversion =
|
|
ic->gray_tables[0].eight_true_conversion;
|
|
is->free_dither_table = 0; /* don't free it its a copied pointer */
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < TCOUNT; i++)
|
|
is->dither_table[i].eight_true_conversion =
|
|
ccf_gray_to_gray_conversion_convert(ic->gray_tables[0].
|
|
eight_true_conversion,
|
|
image->rgb.used,
|
|
maps);
|
|
is->free_dither_table = TCOUNT;
|
|
}
|
|
is->dither_function = &ddf_convert_line_8;
|
|
is->dithering = false;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Reduce 24-bit color to grayscale without dithering
|
|
*/
|
|
for (i = 0; i < TCOUNT; i++)
|
|
is->dither_table[i].eight_true_conversion =
|
|
ic->gray_tables[0].eight_true_conversion;
|
|
is->free_dither_table = 0; /* don't free it its a copied pointer */
|
|
is->dither_function = &ddf_gray_convert_line_24;
|
|
is->dithering = true;
|
|
}
|
|
}
|
|
|
|
maps[0] = maps[1] = maps[2] = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* I'm going to need this soon:
|
|
|
|
*special_return =
|
|
cube_table.true_true_conversion->pixel_values[0][intensities[0] >> 8] +
|
|
cube_table.true_true_conversion->pixel_values[1][intensities[1] >> 8] +
|
|
cube_table.true_true_conversion->pixel_values[2][intensities[2] >> 8];
|
|
*
|
|
*/
|
|
|
|
|
|
static void
|
|
lf_get_new_specials(is, ic, image, input)
|
|
ImageState *is;
|
|
ImageClass *ic;
|
|
Image *image;
|
|
byte *input;
|
|
{
|
|
bool *blacklist = is->special_blacklist;
|
|
bool *real_blacklist = is->special_real_blacklist;
|
|
/*
|
|
* Find 3 identical pixels and win a new special color allocation. The
|
|
* pixels are searched for spaced 4 apart, because this ensures that
|
|
* they are not just antialias pixels on the edge of writing or other
|
|
* unimportantly strewn pixels. Also, this catches the case where the
|
|
* GIF has already beed dithered with a 4x4 matrix. This is an important
|
|
* case because it can interfere with our own dithering and produce
|
|
* ugly artefacts.
|
|
*/
|
|
if (!is->expansion_buf &&
|
|
is->dithering &&
|
|
image->type == IRGB &&
|
|
!is->no_more_specials)
|
|
{
|
|
int i;
|
|
for(i = 0; i < image->width - 8; i++, input++)
|
|
{
|
|
byte t = *input;
|
|
bool dont_really_allocate;
|
|
|
|
if (real_blacklist[t]) continue;
|
|
|
|
if (ic->special_count < ic->special_max &&
|
|
is->image_special_count < is->image_special_max &&
|
|
t == input[4] &&
|
|
t == input[8])
|
|
{
|
|
real_blacklist[t] = true;
|
|
dont_really_allocate = false;
|
|
}
|
|
else
|
|
{
|
|
if(blacklist[t]) continue;
|
|
dont_really_allocate = true;
|
|
}
|
|
blacklist[t] = true;
|
|
{
|
|
Intensity intensities[3];
|
|
bool really_allocated;
|
|
unsigned long special_entry;
|
|
|
|
if(t == image->transparent) continue;
|
|
|
|
intensities[0] = image->rgb.red[t];
|
|
intensities[1] = image->rgb.green[t];
|
|
intensities[2] = image->rgb.blue[t];
|
|
if (!xccf_allocate_special(is->dpy,
|
|
is->cmap,
|
|
ic->colorcube,
|
|
ic->grayscale,
|
|
intensities,
|
|
&really_allocated,
|
|
dont_really_allocate,
|
|
ic->special_colors,
|
|
ic->special_count,
|
|
&special_entry))
|
|
{
|
|
/*
|
|
* If a color allocation fails once, we won't bother the
|
|
* X server again. Color allocation is a slow round trip
|
|
*/
|
|
if (!dont_really_allocate)
|
|
is->image_special_max = is->image_special_count;
|
|
}
|
|
else
|
|
{
|
|
int j;
|
|
if(really_allocated)
|
|
{
|
|
ic->special_count++;
|
|
is->image_special_count++;
|
|
if(is->image_special_count >= image->rgb.used)
|
|
is->no_more_specials = true;
|
|
for(j = 0; j < 256; j++)
|
|
blacklist[j] = false;
|
|
}
|
|
for(j = 0; j < TCOUNT; j++)
|
|
{
|
|
ccf_set_specially_allocated(is->dither_table[j].eight_8_dither,
|
|
t, special_entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
SetSize(is)
|
|
ImageState *is;
|
|
{
|
|
unsigned int fw, fh;
|
|
XImage *xi = is->xi;
|
|
bool sbstate;
|
|
|
|
if (GUIGetDimensions(is->wd, &fw, &fh) == -1)
|
|
{
|
|
GUISetScrollBar(is->wd, false);
|
|
GUISetInitialDimensions(is->wd, xi->width, xi->height);
|
|
GUISetDimensions(is->wd, xi->width, xi->height);
|
|
}
|
|
else
|
|
{
|
|
if (xi->width > fw || xi->height > fh) sbstate = true;
|
|
else sbstate = false;
|
|
GUISetScrollBar(is->wd, sbstate);
|
|
GUISetDimensions(is->wd, xi->width, xi->height);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ImageToXImage(pointer, fline, lline)
|
|
void *pointer;
|
|
int fline;
|
|
int lline;
|
|
{
|
|
ImageState *is = (ImageState *)pointer;
|
|
ImageClass *ic = is->ic;
|
|
byte *input;
|
|
char *output;
|
|
int depth;
|
|
int line;
|
|
Image *image = lf_get_image(is);
|
|
int image_type = image->type;
|
|
|
|
/*
|
|
* First time
|
|
*/
|
|
if (is->xi == NULL)
|
|
{
|
|
/*
|
|
* No image gets more than half the special colors
|
|
*/
|
|
is->image_special_max = (ic->special_max - ic->special_count) / 2;
|
|
|
|
if (image->pixlen == 1) depth = 1;
|
|
else depth = is->depth;
|
|
|
|
if(image->depth == 1)
|
|
{
|
|
lf_set_up_bitmap(is);
|
|
if(is->expansion_buf) lf_set_up_expansion(is);
|
|
image_type = is->expanded_type;
|
|
}
|
|
else
|
|
{
|
|
is->expanded_type = image->type;
|
|
}
|
|
|
|
if (is->expansion_buf) depth = is->depth;
|
|
|
|
if (image_type == IRGB || image_type == ITRUE)
|
|
{
|
|
if (!ic->colorcube)
|
|
{
|
|
ic->colorcube =
|
|
xccf_allocate_cube(is->dpy, is->cmap, is->v, is->depth);
|
|
}
|
|
}
|
|
if ((image_type == IGRAY || !ic->colorcube) && image->pixlen != 1)
|
|
{
|
|
if (!ic->grayscale)
|
|
{
|
|
ic->grayscale =
|
|
xccf_allocate_grays(is->dpy, is->cmap, is->v, is->depth);
|
|
}
|
|
}
|
|
|
|
is->xi = XCreateImage(is->dpy, is->v,
|
|
depth,
|
|
depth == 1 ? XYBitmap : ZPixmap,
|
|
0, NULL,
|
|
image->width, image->height,
|
|
32, 0);
|
|
|
|
/* Make sure we have plenty of padding at the end of each line */
|
|
is->xi->bytes_per_line += 16;
|
|
is->xi->data = (char *)MPCGet(is->mp,
|
|
image->height * is->xi->bytes_per_line);
|
|
|
|
#ifdef CHIMERA_LITTLE_ENDIAN
|
|
is->xi->byte_order = is->xi->bitmap_bit_order = LSBFirst;
|
|
#else
|
|
is->xi->byte_order = is->xi->bitmap_bit_order = MSBFirst;
|
|
#endif
|
|
|
|
if(depth != 1) XAddPixel(is->xi, is->bgcolor.pixel);
|
|
if(image->depth != 1 || is->expansion_buf) lf_set_up_dither(is);
|
|
|
|
SetSize(is);
|
|
}
|
|
|
|
/*
|
|
* Every time
|
|
*/
|
|
input = image->data + image->bytes_per_line * fline;
|
|
output = is->xi->data + is->xi->bytes_per_line * fline;
|
|
|
|
/*
|
|
lf_get_new_specials(is, ic, image, input);
|
|
*/
|
|
|
|
for (line = fline;
|
|
line <= lline;
|
|
line++, input += image->bytes_per_line,
|
|
output += is->xi->bytes_per_line)
|
|
{
|
|
if (image->pixlen == 1 && !is->expansion_buf)
|
|
{
|
|
memcpy(output, input,
|
|
MIN(is->xi->bytes_per_line, image->bytes_per_line));
|
|
}
|
|
else
|
|
{
|
|
byte *expanded_input;
|
|
if (is->expansion_buf)
|
|
{
|
|
is->expansion_function(is->expansion_buf,
|
|
input,
|
|
image->width,
|
|
image->rgb.red,
|
|
image->rgb.green,
|
|
image->rgb.blue,
|
|
image->transparent,
|
|
(Intensity)is->bgcolor.red,
|
|
(Intensity)is->bgcolor.green,
|
|
(Intensity)is->bgcolor.blue);
|
|
expanded_input = is->expansion_buf;
|
|
}
|
|
else
|
|
{
|
|
expanded_input = input;
|
|
}
|
|
if(is->dither_function)
|
|
{
|
|
is->dither_function(is->dither_table[line & 3],
|
|
expanded_input,
|
|
output,
|
|
is->xi->bits_per_pixel,
|
|
image->width);
|
|
}
|
|
else
|
|
{
|
|
printf("Warning:no dither function defined for image\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Paint line
|
|
*/
|
|
if (is->xi->depth == 1)
|
|
{
|
|
XSetForeground(is->dpy, ic->gc, is->fg);
|
|
XSetBackground(is->dpy, ic->gc, is->bg);
|
|
}
|
|
XPutImage(is->dpy, is->win, ic->gc, is->xi,
|
|
0, fline,
|
|
0, fline,
|
|
is->xi->width, lline - fline + 1);
|
|
|
|
if (lline > is->last_line) is->last_line = lline;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* ImageClassInit
|
|
*/
|
|
static void
|
|
ImageClassInit(ic, dpy)
|
|
ImageClass *ic;
|
|
Display *dpy;
|
|
{
|
|
/* XGCValues xgcv; */
|
|
|
|
ic->icount = 0;
|
|
ic->init = True;
|
|
ic->gc = XCreateGC(dpy, RootWindow(dpy, DefaultScreen(dpy)), 0, NULL);
|
|
|
|
if (getenv("CHIMERA_MAX_SPECIAL_COLORS"))
|
|
{
|
|
ic->special_max = atoi(getenv("CHIMERA_MAX_SPECIAL_COLORS"));
|
|
}
|
|
else
|
|
{
|
|
ic->special_max = 64;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* ImageDestroy
|
|
*/
|
|
static void
|
|
ImageDestroy(closure)
|
|
void *closure;
|
|
{
|
|
ImageState *is = (ImageState *)closure;
|
|
int i;
|
|
|
|
if (is->if_vector.destroyProc && is->if_vector.image_format_closure != NULL)
|
|
{
|
|
is->if_vector.destroyProc(is->if_vector.image_format_closure);
|
|
is->if_vector.image_format_closure = NULL;
|
|
}
|
|
if(is->xi)
|
|
{
|
|
is->xi->data = NULL;
|
|
XDestroyImage(is->xi);
|
|
is->xi = 0;
|
|
}
|
|
for(i = 0; i < is->free_dither_table; i++)
|
|
{
|
|
if(is->dither_table[i].generic_dither_table)
|
|
free_mem(is->dither_table[i].generic_dither_table);
|
|
is->dither_table[i].generic_dither_table = 0;
|
|
}
|
|
for( ; i < TCOUNT; i++)
|
|
is->dither_table[i].generic_dither_table = 0;
|
|
is->free_dither_table = 0;
|
|
|
|
if (--is->ic->icount == 0)
|
|
{
|
|
/* free specially allocated colors here? */
|
|
}
|
|
|
|
MPDestroy(is->mp);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* ImageInit
|
|
*
|
|
* This is where the initialization for a frame begins. It is
|
|
* called when the content-type becomes known.
|
|
*/
|
|
void *
|
|
ImageInit(wn, class_closure, state)
|
|
ChimeraRender wn;
|
|
void *class_closure;
|
|
void *state; /* ignored */
|
|
{
|
|
ImageClass *ic = (ImageClass *)class_closure;
|
|
ImageState *is;
|
|
int format;
|
|
XWindowAttributes xwa;
|
|
int status;
|
|
char *content;
|
|
int i;
|
|
ChimeraSink wp;
|
|
ChimeraGUI wd;
|
|
MemPool mp;
|
|
|
|
wp = RenderToSink(wn);
|
|
wd = RenderToGUI(wn);
|
|
|
|
content = SinkGetInfo(wp, "content-type");
|
|
|
|
for (i = 0; content_map[i].name != NULL; i++)
|
|
{
|
|
if (strcasecmp(content_map[i].name, content) == 0) break;
|
|
}
|
|
if (content_map[i].name == NULL) return(NULL);
|
|
format = content_map[i].id;
|
|
|
|
mp = MPCreate();
|
|
is = (ImageState *)MPCGet(mp, sizeof(ImageState));
|
|
is->mp = mp;
|
|
|
|
is->win = GUIToWindow(wd);
|
|
is->dpy = GUIToDisplay(wd);
|
|
|
|
if (!ic->init) ImageClassInit(ic, is->dpy);
|
|
|
|
status = XGetWindowAttributes(is->dpy, is->win, &xwa);
|
|
is->v = xwa.visual;
|
|
is->depth = xwa.depth;
|
|
is->cmap = xwa.colormap;
|
|
is->ic = ic;
|
|
is->wd = wd;
|
|
is->wp = wp;
|
|
is->bgcolor.pixel = GUIBackgroundPixel(wd);
|
|
XQueryColor(is->dpy, is->cmap, &(is->bgcolor));
|
|
|
|
if (format == IMAGE_GIF) gifInit(ImageToXImage, is, &is->if_vector);
|
|
else if (format == IMAGE_PNM) pnmInit(ImageToXImage, is, &is->if_vector);
|
|
else if (format == IMAGE_XBM) xbmInit(ImageToXImage, is, &is->if_vector);
|
|
#ifdef HAVE_JPEG
|
|
else if (format == IMAGE_JPEG) jpegInit(ImageToXImage, is, &is->if_vector);
|
|
#endif
|
|
#ifdef HAVE_PNG
|
|
else if (format == IMAGE_PNG) pngInit(ImageToXImage, is, &is->if_vector);
|
|
#endif
|
|
|
|
ic->icount++;
|
|
|
|
return(is);
|
|
}
|
|
|
|
/*
|
|
* ImageAdd
|
|
*/
|
|
static void
|
|
ImageAdd(closure)
|
|
void *closure;
|
|
{
|
|
ImageState *is = (ImageState *)closure;
|
|
byte *data;
|
|
size_t len;
|
|
MIMEHeader mh;
|
|
|
|
SinkGetData(is->wp, &data, &len, &mh);
|
|
|
|
if (is->if_vector.addDataProc && is->if_vector.image_format_closure)
|
|
{
|
|
is->if_vector.addDataProc(is->if_vector.image_format_closure,
|
|
data, len, false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* ImageEnd
|
|
*/
|
|
static void
|
|
ImageEnd(closure)
|
|
void *closure;
|
|
{
|
|
ImageState *is = (ImageState *)closure;
|
|
byte *data;
|
|
size_t len;
|
|
MIMEHeader mh;
|
|
|
|
SinkGetData(is->wp, &data, &len, &mh);
|
|
|
|
if (is->if_vector.addDataProc && is->if_vector.image_format_closure)
|
|
{
|
|
is->if_vector.addDataProc(is->if_vector.image_format_closure,
|
|
data, len, true);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static bool
|
|
ImageExpose(closure, ex, ey, ewidth, eheight)
|
|
void *closure;
|
|
int ex, ey;
|
|
unsigned int ewidth, eheight;
|
|
{
|
|
ImageState *is = (ImageState *)closure;
|
|
unsigned int height;
|
|
|
|
if (is->xi == NULL) return(true);
|
|
|
|
if (ey > is->last_line) return(true);
|
|
|
|
if (ey + eheight > is->last_line) height = is->last_line - ey;
|
|
else height = eheight;
|
|
|
|
XPutImage(is->dpy, is->win, is->ic->gc, is->xi,
|
|
ex, ey,
|
|
ex, ey,
|
|
ewidth, height + 1);
|
|
|
|
return(true);
|
|
}
|
|
|
|
static void
|
|
ImageClassDestroy(closure)
|
|
void *closure;
|
|
{
|
|
ImageClass *ic = (ImageClass *)closure;
|
|
int i;
|
|
|
|
ic->refcount--;
|
|
if (ic->refcount > 0) return;
|
|
|
|
free_mem(ic->colorcube);
|
|
free_mem(ic->grayscale);
|
|
for (i = 0; i < TCOUNT; i++)
|
|
{
|
|
if (ic->color_tables[i].generic_dither_table != NULL)
|
|
{
|
|
free_mem(ic->color_tables[i].generic_dither_table);
|
|
}
|
|
}
|
|
for (i = 0; i < TCOUNT; i++)
|
|
{
|
|
if (ic->gray_tables[i].generic_dither_table != NULL)
|
|
{
|
|
free_mem(ic->gray_tables[i].generic_dither_table);
|
|
}
|
|
}
|
|
MPDestroy(ic->mp);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ImageCancel(closure)
|
|
void *closure;
|
|
{
|
|
return;
|
|
}
|
|
|
|
int
|
|
InitModule_Image(cres)
|
|
ChimeraResources cres;
|
|
{
|
|
ChimeraRenderHooks rh;
|
|
ImageClass *ic;
|
|
int i;
|
|
MemPool mp;
|
|
|
|
mp = MPCreate();
|
|
ic = (ImageClass *)MPCGet(mp, sizeof(ImageClass));
|
|
ic->mp = mp;
|
|
|
|
for (i = 0; content_map[i].name != NULL; i++)
|
|
{
|
|
memset(&rh, 0, sizeof(ChimeraRenderHooks));
|
|
rh.content = content_map[i].name;
|
|
rh.class_context = ic;
|
|
ic->refcount++;
|
|
rh.class_destroy = ImageClassDestroy;
|
|
rh.init = ImageInit;
|
|
rh.add = ImageAdd;
|
|
rh.end = ImageEnd;
|
|
rh.destroy = ImageDestroy;
|
|
rh.cancel = ImageCancel;
|
|
rh.expose = ImageExpose;
|
|
RenderAddHooks(cres, &rh);
|
|
}
|
|
|
|
return(0);
|
|
}
|