hardts%netscape.com ddbb64f57e landing MODULAR_IMGLIB_BRANCH
git-svn-id: svn://10.0.0.236/trunk@6524 18797224-902f-48f8-a5cc-f745e15eee43
1998-07-27 16:14:32 +00:00

664 lines
19 KiB
C++

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/* -*- Mode: C; tab-width: 4 -*-
* xbm.c --- Decoding of X bit-map format images
* $Id: xbm.cpp,v 3.1 1998-07-27 16:09:52 hardts%netscape.com Exp $
*/
#include "if.h"
#define CR '\015'
#define LF '\012'
#include "merrors.h"
#include "il.h"
PR_BEGIN_EXTERN_C
extern int MK_OUT_OF_MEMORY;
PR_END_EXTERN_C
typedef enum {
xbm_gather,
xbm_init,
xbm_width,
xbm_height,
xbm_start_data,
xbm_data,
xbm_hex,
xbm_done,
xbm_oom,
xbm_error
} xstate;
#define MAX_HOLD 512
typedef void(*il_xbm_converter)(void *, unsigned char, unsigned int);
typedef struct xbm_struct {
xstate state;
unsigned char hold[MAX_HOLD];
intn gathern; /* gather n chars */
unsigned char gatherc; /* gather until char c */
intn gathered; /* bytes accumulated */
xstate gathers; /* post-gather state */
uint32 xpos, ypos;
unsigned char *p; /* raster position */
unsigned char *m; /* mask position (if mask is used) */
il_xbm_converter converter; /* colorspace converter */
uint8 bg_pixel; /* destination image background pixel index */
uint8 fg_pixel; /* destination image foreground pixel index */
} xbm_struct;
#define MAX_LINE MAX_HOLD /* XXX really bad xbms will hose us */
#define GETN(n,s) {xs->state=xbm_gather; xs->gathern=n; xs->gathers=s;}
#define GETC(c,s) {xs->state=xbm_gather; xs->gatherc=c; xs->gathers=s;}
char hex_table_initialized = FALSE;
static uint8 hex[256];
static void
il_init_hex_table(void)
{
hex['0'] = 0; hex['1'] = 8;
hex['2'] = 4; hex['3'] = 12;
hex['4'] = 2; hex['5'] = 10;
hex['6'] = 6; hex['7'] = 14;
hex['8'] = 1; hex['9'] = 9;
hex['A'] = hex['a'] = 5;
hex['B'] = hex['b'] = 13;
hex['C'] = hex['c'] = 3;
hex['D'] = hex['d'] = 11;
hex['E'] = hex['e'] = 7;
hex['F'] = hex['f'] = 15;
hex_table_initialized = TRUE;
}
int il_xbm_init(il_container *ic)
{
xbm_struct *xs;
NI_ColorSpace *src_color_space = ic->src_header->color_space;
if (!hex_table_initialized)
il_init_hex_table();
xs = PR_NEWZAP (xbm_struct);
if (xs)
{
xs->state = xbm_init;
xs->gathers = xbm_error;
ic->ds = xs;
}
/* Initialize the container's source image header. */
src_color_space->type = NI_GreyScale;
src_color_space->pixmap_depth = 1;
src_color_space->bit_alloc.index_depth = 1;
src_color_space->cmap.num_colors = 2;
return xs != 0;
}
static int
il_xbm_init_transparency(il_container *ic)
{
IL_IRGB *src_trans_pixel = ic->src_header->transparent_pixel;
IL_IRGB *img_trans_pixel;
if (!src_trans_pixel) {
src_trans_pixel = PR_NEWZAP(IL_IRGB);
if (!src_trans_pixel)
return FALSE;
ic->src_header->transparent_pixel = src_trans_pixel;
/* Initialize the destination image's transparent pixel. */
il_init_image_transparent_pixel(ic);
/* Set the source image's transparent pixel color to be the preferred
transparency color of the destination image. */
img_trans_pixel = ic->image->header.transparent_pixel;
src_trans_pixel->red = img_trans_pixel->red;
src_trans_pixel->green = img_trans_pixel->green;
src_trans_pixel->blue = img_trans_pixel->blue;
/* Set the source image's transparent pixel index. */
src_trans_pixel->index = 1;
}
return TRUE;
}
/* Since the XBM decoder writes directly to the image bits, we need to provide
our own color space conversion routines. ConvertDefault is the preferred
routine from an efficiency standpoint, but it requires the Front Ends to
agree to receive 1-bit deep data. The other routines are provided since
the Front Ends can request the Image Library to decode to the display
colorspace, which may not be 1-bit deep. */
static void
ConvertDefault(void *ds, unsigned char val, unsigned int last_bit_mask)
{
xbm_struct *xs = (xbm_struct *)ds;
*xs->p = val;
xs->p++;
}
static void
ConvertBWToRGB8(void *ds, unsigned char val, unsigned int last_bit_mask)
{
unsigned int bit_mask;
xbm_struct *xs = (xbm_struct *)ds;
uint8 *ptr = (uint8 *)xs->p;
for (bit_mask = 128; bit_mask >= last_bit_mask; bit_mask >>= 1) {
if (val & bit_mask)
*ptr = 0;
else
*ptr = (uint8)~0;
ptr++;
}
xs->p = (unsigned char *)ptr;
}
static void
ConvertBWToRGB16(void *ds, unsigned char val, unsigned int last_bit_mask)
{
unsigned int bit_mask;
xbm_struct *xs = (xbm_struct *)ds;
uint16 *ptr = (uint16 *)xs->p;
for (bit_mask = 128; bit_mask >= last_bit_mask; bit_mask >>= 1) {
if (val & bit_mask)
*ptr = 0;
else
*ptr = (uint16)~0;
ptr++;
}
xs->p = (unsigned char *)ptr;
}
static void
ConvertBWToRGB24(void *ds, unsigned char val, unsigned int last_bit_mask)
{
unsigned int bit_mask;
xbm_struct *xs = (xbm_struct *)ds;
uint8 *ptr = (uint8 *)xs->p;
for (bit_mask = 128; bit_mask >= last_bit_mask; bit_mask >>= 1) {
if (val & bit_mask) {
*ptr++ = 0;
*ptr++ = 0;
*ptr++ = 0;
}
else {
*ptr++ = (uint8)~0;
*ptr++ = (uint8)~0;
*ptr++ = (uint8)~0;
}
}
xs->p = (unsigned char *)ptr;
}
static void
ConvertBWToRGB32(void *ds, unsigned char val, unsigned int last_bit_mask)
{
unsigned int bit_mask;
xbm_struct *xs = (xbm_struct *)ds;
uint32 *ptr = (uint32 *)xs->p;
for (bit_mask = 128; bit_mask >= last_bit_mask; bit_mask >>= 1) {
if (val & bit_mask)
*ptr = 0;
else
*ptr = (uint32)~0;
ptr++;
}
xs->p = (unsigned char *)ptr;
}
static void
ConvertBWToCI(void *ds, unsigned char val, unsigned int last_bit_mask)
{
unsigned int bit_mask;
xbm_struct *xs = (xbm_struct *)ds;
uint8 *ptr = (uint8 *)xs->p;
uint8 bg_pixel = xs->bg_pixel;
uint8 fg_pixel = xs->fg_pixel;
for (bit_mask = 128; bit_mask >= last_bit_mask; bit_mask >>= 1) {
if (val & bit_mask)
*ptr = fg_pixel;
else
*ptr = bg_pixel;
ptr++;
}
xs->p = (unsigned char *)ptr;
}
static void
il_xbm_setup_color_space_converter(il_container *ic)
{
IL_ColorSpace *img_color_space = ic->image->header.color_space;
xbm_struct *xs = (xbm_struct *)ic->ds;
switch (img_color_space->type) {
case NI_GreyScale:
switch (img_color_space->pixmap_depth) {
case 1:
xs->converter = ConvertDefault;
break;
case 8:
xs->converter = ConvertBWToRGB8;
break;
default:
PR_ASSERT(0);
break;
}
break;
case NI_TrueColor:
switch (img_color_space->pixmap_depth) {
case 8:
xs->converter = ConvertBWToRGB8;
break;
case 16:
xs->converter = ConvertBWToRGB16;
break;
case 24:
xs->converter = ConvertBWToRGB24;
break;
case 32:
xs->converter = ConvertBWToRGB32;
break;
default:
PR_ASSERT(0);
break;
}
break;
case NI_PseudoColor:
xs->converter = ConvertBWToCI;
break;
default:
PR_ASSERT(0);
break;
}
}
static void
copyline(char *d, const unsigned char *s)
{
int i=0;
while( i++ < MAX_LINE && *s && *s != LF )
*d++ = *s++;
*d=0;
}
int il_xbm_write(il_container *ic, const unsigned char *buf, int32 len)
{
int status, input_exhausted;
xbm_struct *xs = (xbm_struct *)ic->ds;
const unsigned char *q, *p=buf, *ep=buf+len;
IL_Pixmap *image = ic->image;
IL_Pixmap *mask = ic->mask;
NI_PixmapHeader *img_header = &image->header;
NI_PixmapHeader *mask_header = mask ? &mask->header : NULL;
NI_PixmapHeader *src_header = ic->src_header;
char lbuf[MAX_LINE+1];
/* If this assert fires, chances are the netlib screwed up and
continued to send data after the image stream was closed. */
PR_ASSERT(xs);
if (!xs) {
#ifdef DEBUG
ILTRACE(1,("Netlib just took a shit on the imagelib\n"));
#endif
return MK_IMAGE_LOSSAGE;
}
q = NULL; /* Initialize to shut up gcc warnings */
input_exhausted = FALSE;
while(!input_exhausted)
{
ILTRACE(9,("il:xbm: state %d len %d buf %u p %u ep %u",xs->state,len,buf,p,ep));
switch(xs->state)
{
case xbm_data:
GETN(2,xbm_hex);
break;
case xbm_hex:
{
int num_pixels; /* Number of pixels remaining to be
processed in current scanline. */
unsigned char val = (hex[q[1]]<<4) + hex[q[0]];
if (xs->xpos == 0) {
/* XXX -kevina Locking of the bits pointers is
currently done on per-scanline basis. The XBM
decoder, however, does not process a full scanline
at a time, (only a single byte,) so if we return in
the middle of a scanline, the bits pointers will
remain locked until the next write call. I would
like to eventually make the XBM decoder process a
full scanline at a time, since this would eliminate
the locking anomaly, and it would also permit the
colorspace converters to be modelled along the
lines of (and hence bundled with) the per-scanline
colorspace converters in the core image library. */
#ifdef STANDALONE_IMAGE_LIB
ic->img_cx->img_cb->ControlPixmapBits(ic->img_cx->dpy_cx,
ic->image, IL_LOCK_BITS);
ic->img_cx->img_cb->ControlPixmapBits(ic->img_cx->dpy_cx,
ic->mask, IL_LOCK_BITS);
#else
IMGCBIF_ControlPixmapBits(ic->img_cx->img_cb,
ic->img_cx->dpy_cx,
ic->image, IL_LOCK_BITS);
IMGCBIF_ControlPixmapBits(ic->img_cx->img_cb,
ic->img_cx->dpy_cx,
ic->mask, IL_LOCK_BITS);
#endif /* STANDALONE_IMAGE_LIB */
#ifdef _USD
xs->p = (unsigned char *)image->bits +
(img_header->height-xs->ypos-1) *
(img_header->widthBytes);
if (mask)
xs->m = (unsigned char *)mask->bits +
(mask_header->height-xs->ypos-1) *
(mask_header->widthBytes);
#else
xs->p = (unsigned char *)image->bits +
xs->ypos * (img_header->widthBytes);
if (mask)
xs->m = (unsigned char *)mask->bits +
xs->ypos * (mask_header->widthBytes);
#endif
}
if (mask) {
*xs->m = val;
xs->m++;
}
num_pixels = img_header->width - xs->xpos;
if (num_pixels <= 8) {
xs->converter((void*)xs, val, 1<<(8-num_pixels));
#ifdef STANDALONE_IMAGE_LIB
ic->img_cx->img_cb->ControlPixmapBits(ic->img_cx->dpy_cx,
ic->image, IL_UNLOCK_BITS);
ic->img_cx->img_cb->ControlPixmapBits(ic->img_cx->dpy_cx,
ic->mask, IL_UNLOCK_BITS);
#else
IMGCBIF_ControlPixmapBits(ic->img_cx->img_cb,
ic->img_cx->dpy_cx,
ic->image, IL_UNLOCK_BITS);
IMGCBIF_ControlPixmapBits(ic->img_cx->img_cb,
ic->img_cx->dpy_cx,
ic->mask, IL_UNLOCK_BITS);
#endif /* STANDALONE_IMAGE_LIB */
xs->xpos=0;
xs->ypos++;
/* The image library does not scale XBMs, so if the
requested target height does not match the source
height, we stop when the smaller of the two is
reached. */
if (xs->ypos == src_header->height ||
xs->ypos == img_header->height) {
xs->state = xbm_done;
}
else {
GETC('x',xbm_data);
}
}
else {
xs->converter((void*)xs, val, 1);
xs->xpos+=8;
GETC('x',xbm_data);
}
}
break;
case xbm_init:
GETC(LF,xbm_width);
break;
case xbm_width:
{
copyline(lbuf, q);
if(sscanf(lbuf, "#define %*s %d",
(int *)&ic->src_header->width)==1)
{
GETC(LF,xbm_height);
}
else
{
/* Accept any of CR/LF, CR, or LF.
Allow multiple instances of each. */
GETC(LF,xbm_width);
}
}
break;
case xbm_height:
{
copyline(lbuf, q);
if(sscanf(lbuf, "#define %*s %d",
(int *)&ic->src_header->height)==1)
{
IL_RGB* map;
if (!il_xbm_init_transparency(ic))
return MK_OUT_OF_MEMORY;
if ((status = il_size(ic)) < 0)
{
if (status == MK_OUT_OF_MEMORY)
xs->state = xbm_oom;
else
xs->state = xbm_error;
break;
}
/* Determine the background and foreground pixel
indices of the destination image if this is a
PseudoColor display. Note: it is assumed that
the index of the black pixel in the IL_ColorMap
is 0. */
if (img_header->color_space->type == NI_PseudoColor) {
xs->bg_pixel =
img_header->transparent_pixel->index;
xs->fg_pixel = img_header->color_space->cmap.index ?
img_header->color_space->cmap.index[0] :
il_identity_index_map[0];
}
if (ic->mask) {
mask = ic->mask;
mask_header = &mask->header;
}
if((map = (IL_RGB*)PR_Calloc(2, sizeof(IL_RGB)))!=0)
{
src_header->color_space->cmap.map = map;
map++; /* first index is black */
map->red = src_header->transparent_pixel->red;
map->green = src_header->transparent_pixel->green;
map->blue = src_header->transparent_pixel->blue;
}
GETC('{',xbm_start_data);
il_xbm_setup_color_space_converter(ic);
}
else
{
/* Accept any of CR/LF, CR, or LF.
Allow multiple instances of each. */
GETC(LF,xbm_height);
}
}
break;
case xbm_start_data:
GETC('x',xbm_data);
break;
case xbm_gather:
{
if(xs->gatherc)
{
const unsigned char *s;
/* We may need to look ahead one character, so don't point
at the last character in the buffer. */
for(s = p; s < ep; s++)
{
if (xs->gatherc == LF)
{
if ((s[0] == LF) || (s[0] == CR))
{
xs->gatherc = 0;
break;
}
}
else if (s[0] == xs->gatherc)
{
xs->gatherc = 0;
break;
}
}
if(xs->gatherc)
{
if((xs->gathered+ep-p) > MAX_HOLD)
{
/* we will never find it */
xs->state = xbm_error;
}
else
{
XP_MEMCPY(xs->hold+xs->gathered, p, ep-p);
xs->gathered += ep-p;
input_exhausted = TRUE;
}
}
else
{ /* found it */
if(xs->gathered)
{
XP_MEMCPY(xs->hold+xs->gathered, p, s-p+1);
q = xs->hold;
}
else
{
q = p;
}
p = s+1;
xs->gathered=0;
xs->state=xs->gathers;
}
}
if(xs->gathern)
{
if( (ep - p) >= xs->gathern)
{ /* there is enough */
if(xs->gathered)
{ /* finish a prior gather */
XP_MEMCPY(xs->hold+xs->gathered, p, xs->gathern);
q = xs->hold;
xs->gathered=0;
}
else
{
q = p;
}
p += xs->gathern;
xs->gathern=0;
xs->state=xs->gathers;
}
else
{
XP_MEMCPY(xs->hold+xs->gathered, p, ep-p);
xs->gathered += ep-p;
xs->gathern -= ep-p;
input_exhausted = TRUE;
}
}
}
break;
case xbm_done:
il_partial(ic, 0, img_header->height, 0);
return 0;
case xbm_oom:
ILTRACE(1,("il:xbm: reached oom state"));
return MK_OUT_OF_MEMORY;
case xbm_error:
ILTRACE(2,("il:xbm: reached error state"));
return MK_IMAGE_LOSSAGE;
default:
ILTRACE(0,("il:xbm: unknown state %d", xs->state));
PR_ASSERT(0);
break;
}
}
return 0;
}
void
il_xbm_abort(il_container *ic)
{
if (ic->ds)
{
xbm_struct *xs = (xbm_struct*) ic->ds;
PR_FREEIF(xs);
ic->ds = 0;
}
}
void
il_xbm_complete(il_container *ic)
{
il_xbm_abort(ic);
il_image_complete(ic);
il_frame_complete_notify(ic);
}