/*****
* images.c : XmHTML image loading/manipulation routines.
*
* This file Version $Revision: 1.13 $
*
* Creation date: Tue Dec 24 04:08:22 GMT+0100 1996
* Last modification: $Date: 1999/07/29 01:26:28 $
* By: $Author: sopwith $
* Current State: $State: Exp $
*
* Author: newt
*
* Portions Copyright (C) 1994 by John Bradley. Used by permission.
* Copyright (C) 1994-1997 by Ripley Software Development
* All Rights Reserved
*
* This file is part of the XmHTML Widget Library.
*
* 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.
*
*****/
/* NOTE NOTE NOTE NOTE NOTE
*
* This port to Gtk/Gdk assumes that Gdk has proper support for GdkImages under
* 8 bits per pixel, which right now is not the case. Thus, I have not tested
* it for displays with less than 8 bpp.
*/
/*****
* ChangeLog
* $Log: images.c,v $
* Revision 1.13 1999/07/29 01:26:28 sopwith
*
*
* Fix all warnings.
*
* Revision 1.12 1999/05/24 23:45:33 unammx
* 1999-05-24 Miguel de Icaza
*
* * images.c (imageDefaultProc): Use the colormap from the widget
* (before it was un-initialized).
*
* Revision 1.11 1998/11/09 00:32:31 jaycox
*
* Included alloca where necesary. Fixed some void pointer
* arithmetic. Fixed some variable used as initializer errors.
* replaced gint foo:1; with guint foo:1;
*
* Everything but zvt now compiles on irix 6.5 with sgi's compiler.
*
* Revision 1.10 1998/02/12 03:09:11 unammx
* Merge to Koen's XmHTML 1.1.2 + following fixes:
*
* Wed Feb 11 20:27:19 1998 Miguel de Icaza
*
* * gtk-forms.c (freeForm): gtk_destroy_widget is no longer needed
* with the refcounting changes.
*
* * gtk-xmhtml.c (gtk_xmhtml_remove): Only god knows why I was
* adding the just removed widget.
*
* Revision 1.9 1998/01/10 02:27:02 unammx
* First attempt at fixing the RecomputeColors functions. They are still
* not perfect, as scrollbar colors are affected, too. - Federico
*
* Revision 1.8 1998/01/09 02:15:27 unammx
* OK, fixed the non-working images on 16 bpp. This is done by putting
* in a my_gdk_image_new() that explicitly takes the data, padding and
* bytes_per_line parameters for XCreateImage(). The function is a copy
* of the GDK_IMAGE_NORMAL part of the real gdk_image_new(); it just uses
* the specified parameters instead of the Gdk hardcoded values.
*
* This is needed because by default gdk_image_new() always creates
* images with a 32-bit padding for scanclines. XmHTML, however, uses
* different paddings depending on the image depth.
*
* As a sidenote, does anyone know where to get documentation for XShm?
* I am not using GDK_IMAGE_FASTEST because I do not see a way to pass
* the padding parameter to the XShm functions.
*
* - Federico
*
* Revision 1.7 1998/01/07 01:45:37 unammx
* Gtk/XmHTML is ready to be used by the Gnome hackers now!
* Weeeeeee!
*
* This afternoon:
*
* - Changes to integrate gtk-xmhtml into an autoconf setup.
*
* - Changes to make gtk-xmhtml a library to be used by Gnome
* (simply include
#endif
#include
#include
#include
#include
/* prevent Byte re-declaration */
#if defined(HAVE_LIBPNG) || defined(HAVE_LIBZ)
#include
#endif
#include "XmHTMLP.h"
#include "XmHTMLfuncs.h"
#ifdef WITH_MOTIF
#include "XCCP.h"
#endif
#include "plc.h"
#include
#include
/*** External Function Prototype Declarations ***/
/*** Public Variable Declarations ***/
/*** Private Datatype Declarations ****/
#define DEFAULT_IMG_SUSPENDED 1 /* images suspended icon */
#define DEFAULT_IMG_UNSUPPORTED 2 /* unsupported image icon */
/*** Private Function Prototype Declarations ****/
static XmImageInfo* readGifAnimation(TWidget w, ImageBuffer *ib);
static XmHTMLRawImageData *readImage(TWidget html, ImageBuffer *ib);
static void initAlphaChannels(XmHTMLWidget html, Boolean for_body_image);
static void doAlphaChannel(XmHTMLWidget html, XmHTMLImage *image);
static unsigned long *makeColormap(XmHTMLWidget html, XmHTMLImage *image,
XmImageInfo *info);
static void getImageAttributes(XmHTMLImage *image, String attributes);
static void addImageToList(XmHTMLWidget html, XmHTMLImage *image);
static XmHTMLImage *copyImage(XmHTMLImage *src, String attributes);
static XmHTMLImage *lookForImage(XmHTMLWidget html, String url,
String attributes, Dimension *width, Dimension *height);
static XmImageInfo *imageDefaultProc(TWidget w, XmHTMLRawImageData *img_data,
String url);
static XmImageInfo *animDefaultProc(TWidget w, XmHTMLRawImageData *img_data,
XmHTMLRawImageData *master, int *global_used, int *global_used_size,
Boolean return_global_used, String url);
static XmImageInfo *imageDelayedProc(TWidget w, XmHTMLRawImageData *img_data,
ImageBuffer *ib);
static XmImageInfo *defaultImage(XmHTMLWidget html, String src,
int default_image_type, Boolean call_for_free);
static int getMaxColors(TWidget w, int max_colors);
static void processBodyImage(XmHTMLWidget html, XmHTMLImage *body_image,
Dimension width, Dimension height);
/*****
* This macro updates the dimensions of the word represented by an image.
* The text layout routines in paint.c (SetText) considers text and images to
* be the same object: each is represented by a XmHTMLWord in which the
* dimensions of an object are given by a bounding rectangle. So when an image
* is updated, the bounding rectangle of this word also requires updating.
*
* This is a macro rather than a function 'cause it has the potential of being
* called multiple times.
*
* sanity checks to be satisfied: this image *must* have an owner, this owner
* *must* have a word and this word *must* be an image and this image *must* be
* equal to the current image.
*****/
#define updateImageWord(IMG) { \
if((IMG)->owner != NULL && (IMG)->owner->words != NULL && \
(IMG)->owner->words[0].image == (IMG)) \
{ \
(IMG)->owner->words[0].width = (IMG)->width; \
(IMG)->owner->words[0].height = (IMG)->height; \
} \
}
/*** Private Variable Declarations ***/
/*****
* Name: readImage
* Return Type: Byte*
* Description: image loading driver routine.
* In:
* html: widget id
* ib: image memory buffer.
* width: width of loaded image. Filled upon return
* height: height of loaded image. Filled upon return
* colors: colors used by the image. Filled upon return
* bg: background pixel for this image.
* Returns:
*
* Note:
* X11 bitmaps, pixmaps and gif are supported by default. If the
* system we are running on has the jpeglib, loading of jpeg files is
* also supported. Same holds for png.
*****/
static XmHTMLRawImageData*
readImage(TWidget html, ImageBuffer *ib)
{
XmHTMLRawImageData *img_data = NULL;
RewindImageBuffer(ib);
switch(ib->type)
{
case IMAGE_GIF:
case IMAGE_GZF: /* our compatible gif format */
img_data = _XmHTMLReadGIF(html, ib);
_XmHTMLDebug(6, ("readImage: loaded gif image %s\n", ib->file));
break;
case IMAGE_XBM:
img_data = _XmHTMLReadBitmap(html, ib);
_XmHTMLDebug(6, ("readImage: loaded X11 bitmap image %s\n",
ib->file));
break;
case IMAGE_XPM:
img_data = _XmHTMLReadXPM(html, ib);
_XmHTMLDebug(6, ("readImage: loaded Xpm3 image %s\n", ib->file));
break;
case IMAGE_JPEG:
img_data = _XmHTMLReadJPEG(html, ib);
_XmHTMLDebug(6, ("readImage: loaded jpeg image %s\n", ib->file));
break;
case IMAGE_PNG:
img_data = _XmHTMLReadPNG(html, ib);
_XmHTMLDebug(6, ("readImage: loaded png image %s\n", ib->file));
break;
case IMAGE_FLG: /* treated wholy differently */
break;
case IMAGE_UNKNOWN:
_XmHTMLDebug(6, ("Can't load image %s: unsupported image format?",
ib->file));
default:
break;
}
return(img_data);
}
/*****
* Name: clipImage
* Return Type: void
* Description: clips the given image to the given dimensions
* In:
* image: image to be clipped
* new_w: new width
* new_h: new height
* Returns:
* nothing
* Note:
* modified from xgif-1.2
*****/
static void
clipImage(XmImageInfo *image, Dimension new_w, Dimension new_h)
{
Byte *data, *dataPtr, *imgPtr;
int x, y;
_XmHTMLDebug(6, ("images.c, clipImage, clipping %s. current "
"dimensions: %ix%i, new: %ix%i\n", image->url, image->width,
image->height, new_w, new_h));
/* allocate memory for clipped image data */
dataPtr = data = (Byte *)malloc(new_w*new_h);
/* pick up current image data */
imgPtr = image->data;
/* clipping is done from top to bottom, left to right */
for(y = 0; y < new_h; y++)
{
for(x = 0; x < new_w; x++)
*(dataPtr++) = *(imgPtr++);
/* skip anything outside image width */
while(x < image->width)
imgPtr++;
}
/* free previous (unclipped) image data */
free(image->data);
/* new clipped image data */
image->data = data;
/* new image dimensions */
image->width = new_w;
image->height= new_h;
_XmHTMLDebug(6, ("images.c, clipImage end\n"));
}
/*****
* Name: scaleImage
* Return Type: void
* Description: scales the given image to the given dimensions
* In:
* image: image to be scaled
* new_w: new width
* new_h: new height
* Returns:
* nothing
* Note:
* modified from xgif-1.2
*****/
static void
scaleImage(XmImageInfo *image, Dimension new_w, Dimension new_h)
{
Byte *data, *img_data, *ilptr, *ipptr, *elptr, *epptr;
int ix, iy, ex, ey, src_w, src_h;
_XmHTMLDebug(6, ("images.c, scaleImage, scaling %s. current "
"dimensions: %ix%i, new: %ix%i\n", image->url, image->width,
image->height, new_w, new_h));
/* allocate memory for scaled image data */
data = (Byte *)malloc(new_w*new_h);
/* pick up current image data */
img_data = image->data;
/* current image dimensions */
src_w = image->width;
src_h = image->height;
/* initialize scaling */
elptr = epptr = data;
/* scaling is done from top to bottom, left to right */
for(ey = 0 ; ey < new_h; ey++, elptr += new_w)
{
/* vertical pixel skip */
iy = (src_h * ey) / new_h;
epptr = elptr;
ilptr = img_data + (iy * src_w);
for(ex = 0; ex < new_w; ex++, epptr++)
{
/* horizontal pixel skip */
ix = (src_w * ex) / new_w;
ipptr = ilptr + ix;
*epptr = *ipptr;
}
}
/* free previous (unscaled) image data */
free(img_data);
/* scaled image data */
image->data = data;
/*
* Create new clipmask data if we have one instead of resizing the
* clipmask itself.
*/
if(ImageInfoClipmask(image))
{
int bcnt, clipsize;
clipsize = new_w;
/* make it byte-aligned */
while((clipsize % 8))
clipsize++;
/* this many bytes on a row */
clipsize /= 8;
/* size of clipmask */
clipsize *= new_h;
/* resize clipmask data */
image->clip = (Byte*)realloc(image->clip, clipsize);
/* zero it */
memset(image->clip, 0, clipsize);
ilptr = image->clip;
elptr = image->data;
/* recreate bitmap */
for(iy = 0; iy < new_h; iy++)
{
for(ix = 0, bcnt = 0; ix < new_w; ix++)
{
if(*elptr != (Byte)image->bg)
*ilptr += bitmap_bits[(bcnt % 8)];
if((bcnt % 8) == 7 || ix == (new_w - 1))
ilptr++;
bcnt++;
elptr++;
}
}
}
/* new image width and height */
image->width = new_w;
image->height= new_h;
_XmHTMLDebug(6, ("images.c, scaleImage End\n"));
}
/*****
* Name: getMaxColors
* Return Type: int
* Description: check maximum number of colors allowed for current display.
* In:
* w: widget id;
* max_colors: current setting for maximum image colors.
* Returns:
* maximum number of colors for current display.
*****/
static int
getMaxColors(TWidget w, int max_colors)
{
int ncolors;
#ifdef WITH_MOTIF
TVisual *visual = NULL;
/* get visual for this widget and take maximum colors from there. */
XtVaGetValues(w, XmNvisual, &visual, NULL);
/*
* Get parent visual if current widget doesn't have one. This will
* *always* return a visual.
*/
if(!visual)
visual = XCCGetParentVisual(w);
/* maximum colors supported for this type of visual but no more than 256 */
ncolors = visual->map_entries > MAX_IMAGE_COLORS ?
MAX_IMAGE_COLORS : visual->map_entries;
#else
ncolors = GDK_VISUAL_XVISUAL(gtk_widget_get_visual(w))->map_entries;
#endif
if(max_colors > ncolors)
{
_XmHTMLWarning(__WFUNC__(w, "getMaxColors"),
"Bad value for XmNmaxImageColors: %i colors selected while "
"display only\n supports %i colors. Reset to %i",
max_colors, ncolors, ncolors);
return(ncolors);
}
else if(!max_colors)
return(ncolors);
return(max_colors);
}
/*****
* A function used when an XImage can not be created for this type of
* display (depth and/or bits_per_pixel).
*****/
static TXImage*
XImageBizarre(XmHTMLWidget html, int depth, TXImage *ximage)
{
#ifdef WITH_MOTIF
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLCreateXImage"),
"This display's too bizarre (depth = %d bits, bits per pixel = %d).\n"
" Can't create XImage.", depth, ximage->bits_per_pixel);
#else
/* XXX: bytes per pixel instead of bits per pixel; need image
* support from Gdk
*/
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLCreateXImage"),
"This display's too bizarre (depth = %d bits, bytes per pixel = %d).\n"
" Can't create XImage.", depth, ximage->bpp);
#endif
Toolkit_Image_Destroy(ximage);
return((TXImage*)NULL);
}
#ifndef WITH_MOTIF
static void
my_gdk_image_put_normal (GdkDrawable *drawable,
GdkGC *gc,
GdkImage *image,
gint xsrc,
gint ysrc,
gint xdest,
gint ydest,
gint width,
gint height)
{
GdkWindowPrivate *drawable_private;
GdkImagePrivate *image_private;
GdkGCPrivate *gc_private;
g_return_if_fail (drawable != NULL);
g_return_if_fail (image != NULL);
g_return_if_fail (gc != NULL);
drawable_private = (GdkWindowPrivate*) drawable;
image_private = (GdkImagePrivate*) image;
gc_private = (GdkGCPrivate*) gc;
g_return_if_fail (image->type == GDK_IMAGE_NORMAL);
XPutImage (drawable_private->xdisplay, drawable_private->xwindow,
gc_private->xgc, image_private->ximage,
xsrc, ysrc, xdest, ydest, width, height);
}
static GdkImage *
my_gdk_image_new(GdkVisual *visual,
gint width,
gint height,
char *data,
gint padding,
gint bytes_per_line)
{
GdkImage *image;
GdkImagePrivate *private;
Visual *xvisual;
private = g_new (GdkImagePrivate, 1);
image = (GdkImage*) private;
private->xdisplay = gdk_display;
private->image_put = my_gdk_image_put_normal;
image->type = GDK_IMAGE_NORMAL;
image->visual = visual;
image->width = width;
image->height = height;
image->depth = visual->depth;
xvisual = ((GdkVisualPrivate*) visual)->xvisual;
private->ximage = XCreateImage (private->xdisplay, xvisual, visual->depth,
ZPixmap, 0, data, width, height, padding, bytes_per_line);
image->byte_order = private->ximage->byte_order;
image->mem = private->ximage->data;
image->bpl = private->ximage->bytes_per_line;
switch (private->ximage->bits_per_pixel)
{
case 8:
image->bpp = 1;
break;
case 16:
image->bpp = 2;
break;
case 24:
image->bpp = 3;
break;
case 32:
image->bpp = 4;
break;
}
return image;
}
#endif
/*****
* Name: _XmHTMLCreateXImage
* Return Type: XImage
* Description: Image XImage creation routine.
* In:
* html: XmHTMLWidget id;
* xcc: XColorContext info for this image;
* width, height:
* dimensions of XImage to be created;
* url: current image identifier
* Returns:
* a newly created XImage with an allocated data member.
*****/
TXImage*
_XmHTMLCreateXImage(XmHTMLWidget html, XCC xcc, Dimension width,
Dimension height, String url)
{
int depth = XCCGetDepth (xcc);
TVisual *vis = xcc->visual;
static TXImage *ximage = NULL;
_XmHTMLDebug(6, ("images.c: _XmHTMLCreateXImage, creating XImage\n"));
/* branch to correct display depth */
switch(depth)
{
case 1:
{
Byte *data;
/* XXX: Is a 1-bit deep XYPixmap the same as a 1-bit ZPixmap? */
ximage = Toolkit_Create_Image(dpy, vis, depth, XYPixmap, 0, NULL,
width, height, 32, 0);
data = (Byte*)malloc(Toolkit_Image_Bytes_Per_Line(ximage) * height);
Toolkit_Set_Image_Data(ximage, (char *)data);
/*****
* FIXME
* Add code to dither this image down to 1bit depth.
*****/
}
break;
case 2:
{
Byte *data;
int bpp;
ximage = Toolkit_Create_Image(dpy, vis, depth, ZPixmap, 0, NULL,
width, height, 8, 0);
bpp = Toolkit_Image_Bits_Per_Pixel(ximage);
if(bpp != 2 && bpp != 4 && bpp != 8)
return(XImageBizarre(html, depth, ximage));
data = (Byte*)malloc(Toolkit_Image_Bytes_Per_Line(ximage) * height);
Toolkit_Set_Image_Data(ximage, (char*)data);
}
break;
case 4:
{
Byte *data;
int bpp;
ximage = Toolkit_Create_Image(dpy, vis, depth, ZPixmap, 0, NULL,
width, height, 8, 0);
bpp = Toolkit_Image_Bits_Per_Pixel(ximage);
if(bpp != 4 && bpp != 8)
return(XImageBizarre(html, depth, ximage));
data = (Byte*)malloc(Toolkit_Image_Bytes_Per_Line(ximage) * height);
Toolkit_Set_Image_Data(ximage, (char*)data);
}
break;
case 5:
case 6:
{
Byte *data;
ximage = Toolkit_Create_Image(dpy, vis, depth, ZPixmap, 0, NULL,
width, height, 8, 0);
if(Toolkit_Image_Bits_Per_Pixel(ximage) != 8)
return(XImageBizarre(html, depth, ximage));
data = (Byte*)malloc(Toolkit_Image_Bytes_Per_Line(ximage) * height);
Toolkit_Set_Image_Data(ximage, (char*)data);
}
break;
case 8:
{
Byte *data;
int imWIDE, nullCount;
/* no of padding bytes per line */
nullCount = (4 - (width % 4)) & 0x03;
imWIDE = width + nullCount;
data = (Byte*)malloc(imWIDE * height);
ximage = Toolkit_Create_Image(dpy, vis, depth, ZPixmap, 0,
(char*)data, width, height, 32, imWIDE);
}
break;
case 12:
case 15:
case 16:
{
unsigned short *data;
ximage = Toolkit_Create_Image(dpy, vis, depth, ZPixmap, 0, NULL,
width, height, 16, 0);
if(depth == 12 && Toolkit_Image_Bits_Per_Pixel(ximage) != 16)
return(XImageBizarre(html, depth, ximage));
data = (unsigned short*)malloc(2 * width * height);
Toolkit_Set_Image_Data(ximage, (char*)data);
}
break;
case 24:
case 32:
{
Byte *data;
ximage = Toolkit_Create_Image(dpy, vis, depth, ZPixmap, 0, NULL,
width, height, 32, 0);
data = (Byte*)malloc(4 * width * height);
Toolkit_Set_Image_Data(ximage, (char*)data);
}
break;
default:
{
/* too bad, we refuse to run on this display */
_XmHTMLWarning(__WFUNC__(html, "_PLC_IMG_CreateXImage"),
"no code to handle this display type (%d bits deep)",
depth);
return(NULL);
}
break;
}
/* FIXME: Things will fail later when any of the conditions XmHTML expects
* (the "if (ximage->bits_per_pixel != foo)" stuff above) are not met. This
* has to be solved by adding proper support to Gdk for the image types it
* is missing.
*/
if(ximage == NULL)
{
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLCreateXImage"),
"%s: Internal error:\n Could not create ximage",
url ? url : "(animation frame)");
return(NULL);
}
return(ximage);
}
/*****
* Name: _XmHTMLFillXImage
* Return Type: void
* Description: Image data->ximage transfer function
* In:
* html: XmHTMLWidget id;
* ximage: ximage to be filled;
* xcc: XColorContext for this image;
* data: raw image data;
* xcolors: array of allocated pixel values;
* *start: starting index in data, or NULL;
* *end: ending index in data, or NULL. If non-NULL updated upon
* return to align on scanline boundary;
* Returns:
* nothing.
*****/
void
_XmHTMLFillXImage(XmHTMLWidget html, TXImage *ximage, XCC xcc, Byte *data,
unsigned long *xcolors, int *start, int *end)
{
int hi, lo;
unsigned int wide, high;
unsigned long xcol;
register int i;
wide = ximage->width;
high = ximage->height;
/*
* hi is max. no of bytes available. lo is min. no of bytes available.
* Any data lower than lo has already been processed, so we do not do
* that again.
*/
if(start != NULL)
{
lo = *start;
hi = *end;
}
else
{
hi = wide*high;
lo = 0;
}
_XmHTMLDebug(6, ("images.c: _XmHTMLFillXImage, doing scanline %i to %i "
"(bytes %i to %i)\n", lo/wide, hi/wide, lo, hi));
#ifdef WITH_MOTIF
switch(xcc->visualInfo->depth)
#else
switch(xcc->visual->depth)
#endif
{
case 8:
{
Byte *imagedata;
int imWIDE, nullCount;
register int j;
register Byte *ip, *pp;
/* XXX: this nullCount calculation should work in Gdk because GdkImage
* always uses 32-bit padding of scanlines. I don't know why this code
* does not simply use bytes_per_line and such.
*/
/* # of padding bytes per line */
nullCount = (4 - (wide % 4)) & 0x03;
imWIDE = wide + nullCount;
lo /= wide; /* starting scanline index (image data) */
hi /= wide; /* ending scanline index (image data) */
/*
* lo*imWIDE contains the no of XImage data bytes already
* processed.
*/
imagedata = (Byte*)Toolkit_Get_Image_Data(ximage) + lo*imWIDE;
pp = data;
for(i = lo, ip = imagedata; i < hi; i++)
{
for(j = 0; j < wide; j++, ip++, pp++)
*ip = (Byte) xcolors[*pp];
for(j = 0; j < nullCount; j++, ip++)
*ip = 0;
}
}
break;
case 4:
{
Byte *imagedata, *lip, *ip;
int bperline;
register int j, half;
register Byte *pp;
bperline = Toolkit_Image_Bytes_Per_Line(ximage);
pp = data;
#ifdef WITH_MOTIF
if(ximage->bits_per_pixel == 4)
#else
/* XXX: This will always fail; need image support from Gdk */
if (FALSE)
#endif
{
lo /= wide; /* starting scanline index (image data) */
hi /= wide; /* ending scanline index (image data) */
/* compute offset into XImage data */
imagedata = (Byte*)Toolkit_Get_Image_Data(ximage) + lo*bperline;
for(i = lo, lip = imagedata; i < hi; i++, lip += bperline)
{
for(j = 0, ip = lip, half = 0; j < wide; j++, pp++, half++)
{
xcol = xcolors[*pp] & 0x0f;
#ifdef WITH_MOTIF
if(ImageByteOrder(dpy) == TLSBFirst)
#else
if(ximage->byte_order == GDK_LSB_FIRST)
#endif
{
if(half&1)
{
*ip = *ip + (xcol<<4);
ip++;
}
else
*ip = xcol;
}
else
{
if(half&1)
{
*ip = *ip + xcol;
ip++;
}
else
*ip = xcol << 4;
}
}
}
}
else /* ximage->bits_per_pixel == 8 */
{
/* compute offset into XImage data */
imagedata = (Byte*)Toolkit_Get_Image_Data(ximage) + lo;
for(i = hi, ip = imagedata; i > lo; i--, pp++, ip++)
*ip = (Byte) xcolors[*pp];
}
}
break;
case 2:
{
Byte *imagedata, *lip, *ip;
int bperline;
register int j, half;
register Byte *pp;
bperline = Toolkit_Image_Bytes_Per_Line(ximage);
pp = data;
#ifdef WITH_MOTIF
if(ximage->bits_per_pixel == 2)
#else
/* XXX: this will always fail; need image support from Gdk */
if (FALSE)
#endif
{
lo /= wide; /* starting scanline index (image data) */
hi /= wide; /* ending scanline index (image data) */
imagedata = (Byte*)Toolkit_Get_Image_Data(ximage) + lo*bperline;
for(i = lo, lip = imagedata; i < hi; i++, lip += bperline)
{
for(j = 0, ip = lip, half=0; j < wide; j++, pp++, half++)
{
xcol = xcolors[*pp] & 0x03;
#ifdef WITH_MOTIF
if (ximage->bitmap_bit_order == TLSBFirst)
#else
if (((GdkImagePrivate *)ximage)->ximage->bitmap_bit_order == TLSBFirst)
#endif
{
if(half%4==0)
*ip = xcol;
else if (half%4==1)
*ip |= (xcol<<2);
else if (half%4==2)
*ip |= (xcol<<4);
else
{
*ip |= (xcol<<6);
ip++;
}
}
else
{
/* MSBFirst. NeXT, among others */
if(half%4==0)
*ip = (xcol<<6);
else if (half%4==1)
*ip |= (xcol<<4);
else if (half%4==2)
*ip |= (xcol<<2);
else
{
*ip |= xcol;
ip++;
}
}
}
}
}
else
{
#ifdef WITH_MOTIF
if(ximage->bits_per_pixel == 4)
#else
/* XXX: this will always fail; need image support from Gdk */
if (FALSE)
#endif
{
lo /= wide; /* starting scanline index (image data) */
hi /= wide; /* ending scanline index (image data) */
imagedata = (Byte*)Toolkit_Get_Image_Data(ximage) + lo*bperline;
for(i = lo, lip = imagedata; i < hi; i++, lip += bperline)
{
for(j = 0, ip = lip, half = 0; j < wide;
j++, pp++, half++)
{
xcol = xcolors[*pp] & 0x0f;
#ifdef WITH_MOTIF
if(ximage->bitmap_bit_order == TLSBFirst)
#else
if(((GdkImagePrivate *)ximage)->ximage->bitmap_bit_order
== TLSBFirst)
#endif
{
if(half&1)
{
*ip |= (xcol<<4);
ip++;
}
else
*ip = xcol;
}
else
{
/* MSBFirst */
if(half&1)
{
*ip |= xcol;
ip++;
}
else
*ip = xcol << 4;
}
}
}
}
else /* ximage->bits_per_pixel == 8 */
{
imagedata = (Byte*)Toolkit_Get_Image_Data(ximage) + lo;
for(i = hi, ip = imagedata; i > lo; i--, pp++, ip++)
*ip = (Byte)xcolors[*pp];
}
}
}
break;
case 5:
case 6:
{
int bperline;
Byte *imagedata;
register Byte *ip, *pp;
bperline = Toolkit_Image_Bytes_Per_Line(ximage);
imagedata = (Byte*)Toolkit_Get_Image_Data(ximage) + (lo/wide)*bperline;
pp = data;
for(i = hi, ip = imagedata; i > lo; i--, pp++, ip++)
*ip = (Byte)xcolors[*pp];
}
break;
case 12:
case 15:
case 16:
{
unsigned short *imagedata;
int bperline;
register unsigned short *ip;
register Byte *pp;
/* 2 bytes per pixel */
bperline = Toolkit_Image_Bytes_Per_Line(ximage);
imagedata = (unsigned short*)Toolkit_Get_Image_Data(ximage) + (lo/wide)*bperline;
pp = data;
if(ximage->byte_order == TMSBFirst)
{
for(i = hi, ip = imagedata; i > lo; i--, pp++)
*ip++ = (unsigned short)(xcolors[*pp] & 0xffff);
}
else
{
/* LSBFirst */
for(i = hi, ip = imagedata; i > lo; i--, pp++)
*ip++ = (unsigned short)(xcolors[*pp]);
}
}
break;
case 24:
case 32:
{
Byte *imagedata, *ip;
int do32, bperline;
register int j;
register Byte *pp, *tip;
bperline = Toolkit_Image_Bytes_Per_Line(ximage);
lo /= wide; /* starting scanline index (image data) */
hi /= wide; /* ending scanline index (image data) */
/* 4 bytes per pixel */
imagedata = (Byte*)Toolkit_Get_Image_Data(ximage) + lo*bperline;
#ifdef WITH_MOTIF
do32 = (ximage->bits_per_pixel == 32);
#else
do32 = (ximage->bpp == 4); /* XXX: bytes per pixel */
#endif
pp = data;
if(ximage->byte_order == TMSBFirst)
{
for(i = lo, ip = imagedata; i < hi; i++)
{
for(j = 0, tip = ip; j < wide; j++, pp++)
{
xcol = xcolors[*pp];
if (do32)
*tip++ = 0;
*tip++ = (xcol>>16) & 0xff;
*tip++ = (xcol>>8) & 0xff;
*tip++ = xcol & 0xff;
}
ip += bperline;
}
}
else
{
/* LSBFirst */
for(i = lo, ip = imagedata; i < hi; i++)
{
for(j = 0, tip = ip; j < wide; j++, pp++)
{
xcol = xcolors[*pp];
*tip++ = xcol & 0xff;
*tip++ = (xcol>>8) & 0xff;
*tip++ = (xcol>>16) & 0xff;
if(do32)
*tip++ = 0;
}
ip += bperline;
}
}
}
break;
default:
break;
}
}
/*****
* Name: makeColormap
* Return Type: int*
* Description: allocates the colors for the given image and creates an
* array of indexed pixel values (which is a sort of private
* colormap).
* In:
* image: internal image data
* info: raw image data. Does not have to be the same as
* image->html_image
* Returns:
* the pixel array.
* Note:
* There's a call to XCCFreeColors in here: when an image is being updated
* or replaced, the XmHTMLImage structure has already got colors allocated
* (by a default image or a previous image) and these must be freed before
* new colors are allocated.
*****/
static unsigned long*
makeColormap(XmHTMLWidget html, XmHTMLImage *image, XmImageInfo *info)
{
static unsigned long *color_map;
image->npixels = 0;
/* allocate color_map pixel entries */
color_map = (unsigned long*)calloc(info->ncolors, sizeof(unsigned long));
XCCGetPixels(image->xcc, info->reds, info->greens, info->blues,
info->ncolors, color_map, &image->npixels);
return(color_map);
}
/*****
* Name: freePixmaps
* Return Type: void
* Description: frees all pixmaps and allocated colors for the given image
* In:
* html: XmHTMLWidget id
* image: image for which to release pixmaps and colors
* Returns:
* nothing.
*****/
static void
freePixmaps(XmHTMLWidget html, XmHTMLImage *image)
{
/* first free all previous pixmaps */
if(image->frames)
{
int i;
for(i = 0; i < image->nframes; i++)
{
FreePixmap(dpy, image->frames[i].pixmap);
FreePixmap(dpy, image->frames[i].clip);
FreePixmap(dpy, image->frames[i].prev_state);
}
if(ImageHasState(image))
{
_XmHTMLDebug(6, ("images.c: freePixmaps, freeing animation state "
"maintainer\n"));
FreePixmap(dpy, image->pixmap);
}
free(image->frames);
image->frames = NULL;
}
else
{
FreePixmap(dpy, image->pixmap);
FreePixmap(dpy, image->clip);
}
image->pixmap = image->clip = TNone;
image->npixels = 0;
}
/*****
* Name: getImageAttributes
* Return Type: void
* Description: retrieves all possible attribute specifications for the
* IMG element.
* In:
* image: image data in which to store the parsed attributes
* attributes: raw attribute specifications to the IMG element
* Returns:
* nothing, but the image structure is updated.
*****/
static void
getImageAttributes(XmHTMLImage *image, String attributes)
{
if((image->alt = _XmHTMLTagGetValue(attributes, "alt")) != NULL)
{
/* handle escape sequences in the alt text */
_XmHTMLExpandEscapes(image->alt, False);
}
else
{
/* if we have a real URL or some path to this image, strip it off */
if(strstr(image->url, "/"))
{
int i;
for(i = strlen(image->url) - 1;
i > 0 && image->url[i] != '/'; i--);
image->alt = strdup(&image->url[i+1]);
}
else /* fix 09/01/97-01, kdh */
image->alt = strdup(image->url);
}
/* we have a border by default */
image->border = _XmHTMLTagGetNumber(attributes, "border", 1);
image->hspace = _XmHTMLTagGetNumber(attributes, "hspace", 0);
image->vspace = _XmHTMLTagGetNumber(attributes, "vspace", 0);
image->align = _XmHTMLGetImageAlignment(attributes);
/*
* Imagemap stuff. First check if we have a usemap spec. If so, it's
* automatically a client-side imagemap. If no usemap is given but the
* ISMAP attribute is set this is a server-side imagemap.
*/
if((image->map_url = _XmHTMLTagGetValue(attributes, "usemap")) != NULL)
image->map_type = XmMAP_CLIENT;
else if(_XmHTMLTagCheck(attributes, "ismap"))
image->map_type = XmMAP_SERVER;
else
image->map_type = XmMAP_NONE;
}
/*****
* Name: copyImage
* Return Type: XmHTMLImage
* Description: links the most wastefull members of src to a new image
* In:
* src: source image
* attributes: attributes for this image
* Returns:
* a copy of src
* Note:
* dest has it's is_copy member set to true which tells XmHTML not to free
* the html_image, xcc and pixmap members of the copy.
*****/
static XmHTMLImage*
copyImage(XmHTMLImage *src, String attributes)
{
static XmHTMLImage *dest;
_XmHTMLDebug(6, ("images.c: copyImage, making copy of %s\n", src->url));
/* we haven't got it yet, create a new one */
dest = (XmHTMLImage*)malloc(sizeof(XmHTMLImage));
/* initialise all fields to zero */
(void)memset(dest, 0, sizeof(XmHTMLImage));
dest->magic = XmHTML_IMAGE_MAGIC;
/* This isn't exactly a copy, but almost a full referential */
dest->url = src->url;
dest->height = src->height;
dest->width = src->width;
dest->sheight = src->sheight;
dest->swidth = src->swidth;
dest->pixmap = src->pixmap;
dest->clip = src->clip;
dest->frames = src->frames;
dest->nframes = src->nframes;
dest->current_frame = src->current_frame;
dest->current_loop = src->current_loop;
dest->loop_count = src->loop_count;
dest->options = src->options;
dest->html_image = src->html_image;
/* unique for each image */
getImageAttributes(dest, attributes);
/* This tells XmHTML which members to free and which not */
dest->options |= IMG_ISCOPY;
return(dest);
}
/*****
* Name: lookForImage
* Return Type: XmHTMLImage
* Description: see if the image at the given url is in the current list of
* images.
* In:
* html: XmHTMLWidget id
* url: image location
* attributes: extra image attributes
* width: possible width specification
* height: possible height specification
* Returns:
* XmHTMLImage structure for this image if found, NULL if not found.
* Note:
* If a match is found, the dimensions of that image are checked against
* possible specifications. When no specifications are given, we can just
* return the image found so a copy can be made. If however specifications
* are given, we need an exact match since I don't know a way to resize
* pixmaps.
*****/
static XmHTMLImage*
lookForImage(XmHTMLWidget html, String url, String attributes,
Dimension *width, Dimension *height)
{
XmHTMLImage *image;
_XmHTMLDebug(6, ("images.c: lookForImage, checking private cache for %s\n",
url));
for(image = html->html.images; image != NULL; image = image->next)
{
/* images that are already a copy can't be copied */
if(image->url && !ImageIsCopy(image) && !(strcmp(image->url, url)))
{
/*
* all possible combinations if dimension specification.
* We always need to compare against the specified dimensions:
* even though the image may be the same, different dimensions
* require it to be scaled. We want to have the original data
* or we would have a tremendous loss of information.
*/
if((!*height && !*width) ||
(*height == image->sheight && *width == image->swidth) ||
(!*height && *width == image->swidth) ||
(!*width && *height == image->sheight))
{
_XmHTMLDebug(6, ("images.c: lookForImage, %s found!\n", url));
if(!*height)
*height = image->sheight;
if(!*width)
*width = image->swidth;
return(image);
}
_XmHTMLDebug(6, ("images.c: lookForImage, %s found but "
"dimensions differ: %i,%i versus %i,%i\n", url,
image->swidth, image->sheight, *width, *height));
}
}
_XmHTMLDebug(6, ("images.c: lookForImage, %s not yet loaded.\n", url));
return(NULL);
}
/*****
* Name: addImageToList
* Return Type: void
* Description: adds the given image to the list of images of a HTML widget
* In:
* html: XmHTMLWidget owning this image
* image: image to store
* Returns:
* nothing.
*****/
static void
addImageToList(XmHTMLWidget html, XmHTMLImage *image)
{
XmHTMLImage *tmp;
/* head of the list */
if(html->html.images == NULL)
{
html->html.images = image;
return;
}
/* walk to the one but last image in the list and insert the image */
for(tmp = html->html.images; tmp != NULL && tmp->next != NULL;
tmp = tmp->next);
tmp->next = image;
}
/*****
* Name: defaultImage
* Return Type: XmImageInfo
* Description: creates an internal image
* In:
* html: XmHTMLWidget
* src: name of image
* default_image_type: type of internal image to create
* Returns:
* XmImageInfo structure for the requested default image
*****/
static XmImageInfo*
defaultImage(XmHTMLWidget html, String src, int default_image_type,
Boolean call_for_free)
{
static XmImageInfo *unsupported, *suspended;
char **xpm_data;
XmHTMLRawImageData *data;
_XmHTMLDebug(6, ("images.c: defaultImage, called for %s image\n",
default_image_type == DEFAULT_IMG_SUSPENDED ?
"suspended" : "unsupported"));
if(default_image_type == DEFAULT_IMG_SUSPENDED)
{
if(call_for_free || suspended != NULL)
return(suspended);
else
xpm_data = boomerang_xpm;
}
else if(default_image_type == DEFAULT_IMG_UNSUPPORTED)
{
if(call_for_free || unsupported != NULL)
return(unsupported);
else
xpm_data = noboomerang_xpm;
}
else
_XmHTMLError(__WFUNC__(html, "defaultImage"), "Internal "
"Error: default image requested but don't know the type!");
data = _XmHTMLCreateXpmFromData((TWidget)html, xpm_data, src);
/*
* The default images *must* use a clipmask: not doing this would
* use the current background for the transparent pixel in every
* document. And since each document doesn't have the same background
* this would render the wrong transparent color when the document
* background color changes.
* Also, these images may *never* bee freed.
*/
if(default_image_type == DEFAULT_IMG_SUSPENDED)
{
suspended = imageDefaultProc((TWidget)html, data, src);
suspended->type = IMAGE_XPM;
suspended->options &= ~XmIMAGE_DEFERRED_FREE;
suspended->options |= XmIMAGE_SHARED_DATA;
suspended->depth = 4; /* 15 colors */
return(suspended);
}
else
{
unsupported = imageDefaultProc((TWidget)html, data, src);
unsupported->type = IMAGE_XPM;
unsupported->options &= ~XmIMAGE_DEFERRED_FREE;
unsupported->options |= XmIMAGE_SHARED_DATA;
unsupported->depth = 4; /* 15 colors */
return(unsupported);
}
}
/*****
* Name: imageDefaultProc
* Return Type: XmImageInfo*
* Description: XmHTML default image loading procedure
* In:
* w: Widget ID;
* img_data: raw image data;
* url: full name and location of image to load
* Returns:
* An XmImageInfo structure on success or NULL on failure
*****/
static XmImageInfo*
imageDefaultProc(TWidget w, XmHTMLRawImageData *img_data, String url)
{
XmImageInfo *image = NULL;
int i, j, cnt, bcnt, size, used[MAX_IMAGE_COLORS];
int bg_red = 0, bg_green = 0, bg_blue = 0, clipsize = 0;
Byte *ptr, *cptr, *clip = NULL;
int do_clip = 0, max_colors = 0;
Boolean clip_valid = False, dither = False;
/* default XmImageInfo flags */
int options = XmIMAGE_DEFERRED_FREE|XmIMAGE_RGB_SINGLE|XmIMAGE_ALLOW_SCALE;
if(XmIsHTML(w))
{
max_colors = ((XmHTMLWidget)w)->html.max_image_colors;
dither = ((XmHTMLWidget)w)->html.map_to_palette != XmDISABLED;
}
else
{
/* check quantization stuff */
if(_xmimage_cfg != NULL && _xmimage_cfg->flags && ImageQuantize)
max_colors = _xmimage_cfg->ncolors;
/* and verify no of colors */
max_colors = getMaxColors(w, max_colors);
}
/* raw data size */
size = img_data->width * img_data->height;
/*
* If we have a background pixel, it means we have a transparent image.
* To make it really transparent, pick up the background pixel and
* corresponding RGB values so it can be substituted in the image.
*/
if(img_data->bg >= 0)
{
_XmHTMLDebug(6, ("images.c: imageDefaultProc, image is transparent\n"));
/*
* We only substitute the background pixel if it has been
* requested explicitly by the ImageBackground flag on the
* global XmImageConfig structure.
*/
if(_xmimage_cfg != NULL)
{
/* Clipmask has priority above bg pixel substitution */
if(_xmimage_cfg->flags & ImageClipmask)
{
options |= XmIMAGE_CLIPMASK;
do_clip = 1;
}
else if(_xmimage_cfg->flags & ImageBackground)
{
TColor bg_color;
_XmHTMLDebug(6, ("images.c: imageDefaultProc, background pixel "
"value for this image: %i\n", img_data->bg));
/* get background color index */
bg_color.pixel = _xmimage_cfg->bg_color;
_XmHTMLDebug(6, ("images.c: imageDefaultProc, provided "
"background pixel is: %li\n", bg_color.pixel));
/* get colormap */
#ifdef WITH_MOTIF
cmap = w->core.colormap;
/* get RGB value for the background pixel */
XQueryColor(Toolkit_Display(w), cmap, &bg_color);
#else
my_x_query_colors(gtk_widget_get_colormap (GTK_WIDGET (w)), &bg_color, 1);
#endif
/* downscale to 0-255 */
bg_color.red >>= 8;
bg_color.green >>= 8;
bg_color.blue >>= 8;
bg_red = img_data->cmap[img_data->bg].red = bg_color.red;
bg_green = img_data->cmap[img_data->bg].green = bg_color.green;
bg_blue = img_data->cmap[img_data->bg].blue = bg_color.blue;
/*****
* We need to allocate a clipmask anyway so we can properly
* restore transparency when we need to quantize the image.
*****/
do_clip = 2;
}
}
else
{
/* XmHTML call or default settings, use a clipmask */
options |= XmIMAGE_CLIPMASK;
do_clip = 1;
}
}
/* we've got ourselves a clipmask */
if(do_clip)
{
i = img_data->width;
/* make it byte-aligned */
while((i % 8))
i++;
/* this many bytes on a row */
i /= 8;
/* size of clipmask */
clipsize = i * img_data->height;
clip = (Byte*)calloc(clipsize, sizeof(Byte));
}
/* allocate an image */
image = (XmImageInfo*)malloc(sizeof(XmImageInfo));
/* initialize to zero */
(void)memset(image, 0, sizeof(XmImageInfo));
/* initialize used colors counter */
memset(&used, 0, MAX_IMAGE_COLORS*sizeof(int));
/* Fill in appropriate fields */
image->url = strdup(url); /* image location */
image->data = img_data->data; /* image data bits */
image->clip = clip; /* clipbitmap bits */
image->type = img_data->type; /* image type */
image->width = img_data->width; /* image width */
image->height = img_data->height; /* image height */
image->swidth = img_data->width; /* original image width */
image->sheight = img_data->height; /* original image height */
image->bg = img_data->bg; /* save background pixel index */
image->options = options; /* set XmImageInfo options */
image->colorspace = (Byte)img_data->color_class;
image->transparency = img_data->bg != -1 ? XmIMAGE_TRANSPARENCY_BG:XmHTML_NONE;
cnt = 1;
cptr = image->clip;
ptr = image->data;
/*
* Fill array of used pixel indexes. If we have a background
* pixel to substitute, save the pixel that should be substituted
* and create the XYBitmap data to be used as a clip mask.
*/
clip_valid = False;
for(i = 0; i < image->height; i++)
{
for(j = 0, bcnt = 0; j < image->width; j++)
{
if(used[(int)*ptr] == 0)
{
used[(int)*ptr] = cnt;
cnt++;
/*
* check for validity of clip data. If we don't have a match
* it means the image has a useless transparency so we
* can safely obliterate the clipmask data.
*/
if(*ptr == image->bg)
clip_valid = True;
}
if(do_clip)
{
if(*ptr != image->bg)
*cptr += bitmap_bits[(bcnt % 8)];
if((bcnt % 8) == 7 || j == (image->width-1))
cptr++;
bcnt++;
}
ptr++;
}
}
cnt--;
/* sanity */
if(cnt > img_data->cmapsize)
{
_XmHTMLWarning(__WFUNC__(w, "imageDefaultProc"), "Detected %i "
"colors in image while %i have been specified! Recovering.",
cnt, img_data->cmapsize);
cnt = img_data->cmapsize;
}
/* store number of colors */
image->ncolors = image->scolors = cnt;
_XmHTMLDebug(6, ("images.c: ImageDefaultProc, counted %i colors in "
"image.\n", cnt));
/*
* erase clipmask if we didn't find a matching pixel while there is
* supposed to be one.
*/
if(do_clip && !clip_valid)
{
image->bg = -1;
do_clip = 0;
free(image->clip);
image->clip = 0;
image->options &= ~XmIMAGE_CLIPMASK;
image->transparency = XmIMAGE_NONE;
_XmHTMLDebug(6, ("images.c: ImageDefaultProc, useless transparency: "
"no matching pixel found, ignoring clip data\n"));
}
/*****
* We must perform dithering (or quantization) *after* we've created a
* (possible) clipmask. Both operations will nuke the transparent pixel...
*****/
if(dither || (max_colors && cnt > max_colors))
{
/*****
* Upon return, image has been dithered/quantized and cmap contains a
* new colormap
*****/
if(dither)
_XmHTMLDitherImage((XmHTMLWidget)w, img_data);
else
_XmHTMLQuantizeImage(img_data, max_colors);
image->data = img_data->data;
/* need to get a new used array */
memset(&used, 0, MAX_IMAGE_COLORS*sizeof(int));
cnt = 1;
ptr = image->data;
for(i = 0; i < image->height; i++)
{
for(j = 0; j < image->width; j++)
{
if(used[(int)*ptr] == 0)
{
used[(int)*ptr] = cnt;
cnt++;
}
ptr++;
}
}
cnt--;
/* store number of colors */
image->ncolors = cnt;
_XmHTMLDebug(6, ("images.c: ImageDefaultProc, image %s "
"from %i to %i colors\n", dither ? "dithered" : "quantized",
image->scolors, image->ncolors));
/*****
* If we have a bg color substitution, quantization messed it up.
* Restore by comparing image and clipmask and pick up the index of
* the new bg pixel. It's given by the first bit in a clipmask which
* is zero.
*****/
if(do_clip == 2)
{
int k;
cptr = image->clip;
ptr = image->data;
image->bg = -1; /* original bg no longer valid */
for(i = 0; i < clipsize && image->bg == -1; i++, cptr++)
{
for(k = 0; k < 8; k++)
{
if(!(*cptr & bitmap_bits[k]))
{
image->bg = (int)*ptr;
break;
}
ptr++;
}
}
/* clipmask no longer needed, free it */
free(image->clip);
image->clip = 0;
do_clip = 0;
_XmHTMLDebug(6, ("images.c: ImageDefaultProc, new background "
"pixel index: %i\n", image->bg));
/* restore background color in colormap */
if(image->bg)
{
img_data->cmap[img_data->bg].red = bg_red;
img_data->cmap[img_data->bg].green = bg_green;
img_data->cmap[img_data->bg].blue = bg_blue;
}
}
}
/* remove saved clipbitmap if it hasn't been used */
if(do_clip == 2)
{
free(image->clip);
image->clip = 0;
image->options &= ~XmIMAGE_CLIPMASK;
image->transparency = XmIMAGE_NONE;
}
/* allocate image RGB values */
image->reds = (Dimension*)calloc(3*cnt,sizeof(Dimension));
image->greens = image->reds + cnt;
image->blues = image->greens + cnt;
/* now go and fill the RGB arrays */
for(i = 0; i < MAX_IMAGE_COLORS; i++)
{
int indx;
if(used[i] != 0)
{
indx = used[i] - 1;
image->reds[indx] = img_data->cmap[i].red;
image->greens[indx] = img_data->cmap[i].green;
image->blues[indx] = img_data->cmap[i].blue;
/*
* Replace transparent color with background RGB values if
* it has been requested.
*/
if(do_clip == 2 && image->bg >= 0 && i == image->bg)
{
image->reds[indx] = bg_red;
image->greens[indx] = bg_green;
image->blues[indx] = bg_blue;
}
}
}
/*
* Final transition step to ZPixmap format
*/
ptr = image->data;
for(i = 0; i < size ; i++)
{
*ptr = (Byte)(used[(int)*ptr] - 1);
ptr++;
}
/* release raw image colormap */
free(img_data->cmap);
/* return loaded image data */
return(image);
}
/*****
* Name: animDefaultProc
* Return Type: XmImageInfo*
* Description: XmHTML default animation loading procedure
* In:
* w: Widget ID;
* img_data: raw image data;
* master: master animation data with global colormap;
* global..: global colormap pixel index array;
* return..: when True, fill the global_used array;
* url: full name and location of image to load. Only valid for the
* first frame in an animation.
* Returns:
* An XmImageInfo structure on success or NULL on failure
*****/
static XmImageInfo*
animDefaultProc(TWidget w, XmHTMLRawImageData *img_data,
XmHTMLRawImageData *master, int *global_used, int *global_used_size,
Boolean return_global_used, String url)
{
XmImageInfo *image = NULL;
int i, j, cnt, bcnt, size, used[MAX_IMAGE_COLORS], clipsize = 0;
int do_clip = 0, max_colors = 0, gcolors;
Byte *ptr, *cptr, *clip = NULL;
Boolean use_local_cmap = False, dither = False;
/* default XmImageInfo flags */
int options = XmIMAGE_DEFERRED_FREE|XmIMAGE_RGB_SINGLE|XmIMAGE_ALLOW_SCALE;
if(XmIsHTML(w))
{
max_colors = ((XmHTMLWidget)w)->html.max_image_colors;
dither = ((XmHTMLWidget)w)->html.map_to_palette != XmDISABLED;
}
else
{
/* check quantization stuff */
if(_xmimage_cfg != NULL && _xmimage_cfg->flags && ImageQuantize)
max_colors = _xmimage_cfg->ncolors;
/* and verify no of colors */
max_colors = getMaxColors(w, max_colors);
}
/* are we to use a local colormap ? */
use_local_cmap = (img_data->cmapsize != 0);
_XmHTMLDebug(6, ("images.c, animDefaultProc, use local colormap : %s\n",
use_local_cmap ? "Yes" : "No"));
/* raw data size */
size = img_data->width * img_data->height;
/* pick up the background pixel and corresponding RGB values */
if(img_data->bg >= 0)
{
options |= XmIMAGE_CLIPMASK;
do_clip = 1;
/* allocate clipmask data */
i = img_data->width;
while((i % 8))
i++;
i /= 8;
clipsize = i * img_data->height;
clip = (Byte*)calloc(clipsize, sizeof(Byte));
}
/* allocate image */
image = (XmImageInfo*)malloc(sizeof(XmImageInfo));
(void)memset(image, 0, sizeof(XmImageInfo));
/* Fill in appropriate fields */
if(url) /* only first frame has this */
image->url = strdup(url);
image->data = img_data->data; /* image data bits */
image->clip = clip; /* clipbitmap bits */
image->type = img_data->type; /* image type */
image->width = img_data->width; /* image width */
image->height = img_data->height; /* image height */
image->swidth = img_data->width; /* original image width */
image->sheight = img_data->height; /* original image height */
image->bg = img_data->bg; /* save background pixel index */
image->options = options; /* set XmImageInfo options */
image->colorspace = (Byte)img_data->color_class;
image->transparency = img_data->bg != -1 ? XmIMAGE_TRANSPARENCY_BG:XmHTML_NONE;
/*****
* Fill array of used pixel indices.
* gcolors is a count of the colors really used by this frame, whether
* or not we should use a global colormap. We must make the distinction
* if we want dithering to be done correctly for each frame.
*****/
if(use_local_cmap || return_global_used)
{
memset(&used, 0, MAX_IMAGE_COLORS*sizeof(int));
cnt = 1;
ptr = image->data;
gcolors = 1;
for(i = 0; i < size; i++, ptr++)
{
if(used[(int)*ptr] == 0)
{
used[(int)*ptr] = cnt++;
gcolors++;
}
}
cnt--;
gcolors--;
}
else
{
/* gused is the array of colors really being used */
int gused[MAX_IMAGE_COLORS];
memcpy(&used, global_used, MAX_IMAGE_COLORS*sizeof(int));
memset(&gused, 0, MAX_IMAGE_COLORS*sizeof(int));
cnt = *global_used_size;
ptr = image->data;
gcolors = 1;
for(i = 0; i < size; i++, ptr++)
{
if(used[(int)*ptr] == 0)
used[(int)*ptr] = cnt++; /* update running counter */
if(gused[(int)*ptr] == 0)
gused[(int)*ptr] = gcolors++;
}
cnt--;
gcolors--;
}
_XmHTMLDebug(6, ("images.c, animDefaultProc, counted %i colors.\n",
gcolors));
/* store number of colors if we are using a local colormap */
if(use_local_cmap)
{
if(cnt > img_data->cmapsize)
cnt = img_data->cmapsize;
image->ncolors = image->scolors = cnt;
}
/* only update global array when we aren't dithering */
else if(return_global_used || *global_used_size < cnt)
{
/* only update if we don't have to quantize this image */
if(return_global_used || !(max_colors && gcolors > max_colors))
{
_XmHTMLDebug(6, ("images.c, animDefaultProc, updating "
"global_used array (old size: %i, new size: %i)\n",
*global_used_size, cnt));
for(i = 0; i < MAX_IMAGE_COLORS; i++)
if(global_used[i] == 0)
global_used[i] = used[i];
}
if(return_global_used)
image->ncolors = image->scolors = gcolors;
}
/* check clipmask stuff */
if(do_clip)
{
cptr = image->clip;
ptr = image->data;
for(i = 0; i < image->height; i++)
{
for(j = 0, bcnt = 0; j < image->width; j++)
{
if(*ptr != image->bg)
*cptr += bitmap_bits[(bcnt % 8)];
if((bcnt % 8) == 7 || j == (image->width-1))
cptr++;
bcnt++;
ptr++;
}
}
}
/* dither/quantize if necessary */
if(dither || (max_colors && gcolors > max_colors))
{
_XmHTMLDebug(6, ("images.c, animDefaultProc, %s from %i to "
"%i colors max\n", dither ? "dithering" : "quantizing", gcolors,
max_colors));
/* if we don't have a local colormap, copy the global one */
if(!use_local_cmap)
{
AllocRawImageCmap(img_data, master->cmapsize);
/* copy it */
memcpy(img_data->cmap, master->cmap,
master->cmapsize * sizeof(XColor));
/* forces use of the local colormap */
use_local_cmap = True;
}
/* do it */
if(dither)
_XmHTMLDitherImage((XmHTMLWidget)w, img_data);
else
_XmHTMLQuantizeImage(img_data, max_colors);
image->data = img_data->data;
/* need to get a new used array */
memset(&used, 0, MAX_IMAGE_COLORS*sizeof(int));
cnt = 1;
ptr = image->data;
/* compose it */
for(i = 0; i < image->height; i++)
{
for(j = 0; j < image->width; j++)
{
if(used[(int)*ptr] == 0)
{
used[(int)*ptr] = cnt;
cnt++;
}
ptr++;
}
}
cnt--;
/* store number of colors */
image->ncolors = cnt;
}
/*****
* Allocate image RGB values if we have a local colormap or when we
* have to return it (for the first frame in this animation)
*****/
if(use_local_cmap)
{
image->reds = (Dimension*)calloc(3*cnt,sizeof(Dimension));
image->greens = image->reds + cnt;
image->blues = image->greens + cnt;
/* now go and fill the RGB arrays */
for(i = 0; i < MAX_IMAGE_COLORS; i++)
{
int indx;
if(used[i] != 0)
{
indx = used[i] - 1;
image->reds[indx] = img_data->cmap[i].red;
image->greens[indx] = img_data->cmap[i].green;
image->blues[indx] = img_data->cmap[i].blue;
}
}
/* can happen when we have quantized the first frame in an animation */
if(return_global_used)
{
memcpy(global_used, &used, MAX_IMAGE_COLORS*sizeof(int));
*global_used_size = cnt;
image->ncolors = cnt;
/*****
* Quantization made a copy of the master colormap. As this is
* the first frame in this animation, we must free the copy here
* or it won't get freed at all.
*****/
if(img_data->cmapsize)
free(img_data->cmap);
img_data->cmapsize = 0;
}
}
else if(return_global_used || *global_used_size < cnt)
{
_XmHTMLDebug(6, ("images.c, animDefaultProc, updating global "
"colormap\n"));
image->reds = (Dimension*)calloc(3*cnt,sizeof(Dimension));
image->greens = image->reds + cnt;
image->blues = image->greens + cnt;
/* now go and fill the RGB arrays */
for(i = 0; i < MAX_IMAGE_COLORS; i++)
{
int indx;
if(global_used[i] != 0)
{
indx = global_used[i] - 1;
image->reds[indx] = master->cmap[i].red;
image->greens[indx] = master->cmap[i].green;
image->blues[indx] = master->cmap[i].blue;
}
}
*global_used_size = cnt;
}
/* Final transition step to ZPixmap format */
ptr = image->data;
if(use_local_cmap)
{
for(i = 0; i < size ; i++)
{
*ptr = (Byte)(used[(int)*ptr] - 1);
ptr++;
}
}
else
{
for(i = 0; i < size ; i++)
{
*ptr = (Byte)(global_used[(int)*ptr] - 1);
ptr++;
}
}
/* release local colormap */
if(img_data->cmapsize && !return_global_used)
{
free(img_data->cmap);
img_data->cmapsize = 0;
}
/* return loaded image data */
return(image);
}
/*****
* Name: imageDelayedProc
* Return Type: XmImageInfo*
* Description: creates an empty XmImageInfo structure required for delayed
* image creation (e.i., images with an alpha channel).
* In:
* w: Widget ID;
* img_data: raw image data;
* ib: current ImageBuffer, contains unprocessed image data.
* Returns:
* An XmImageInfo structure on success or NULL on failure
*****/
static XmImageInfo*
imageDelayedProc(TWidget w, XmHTMLRawImageData *img_data, ImageBuffer *ib)
{
static XmImageInfo *image = NULL;
/* allocate an image */
image = (XmImageInfo*)malloc(sizeof(XmImageInfo));
/* initialize to zero */
(void)memset(image, 0, sizeof(XmImageInfo));
/* store image location and type of this image */
image->url = strdup(ib->file);
image->type = ib->type;
image->depth = ib->depth;
image->width = img_data->width; /* image width, REQUIRED!!! */
image->height = img_data->height; /* image height, REQUIRED!!! */
image->swidth = img_data->width;
image->sheight = img_data->height;
image->ncolors = image->scolors = img_data->cmapsize;
image->bg = -1;
image->transparency = XmIMAGE_TRANSPARENCY_ALPHA;
image->colorspace = img_data->color_class;
image->options = XmIMAGE_DELAYED_CREATION | XmIMAGE_ALLOW_SCALE;
image->fg_gamma = img_data->fg_gamma;
image->alpha = img_data->data;
/* return loaded image data */
return(image);
}
/*****
* Name: _XmHTMLInfoToPixmap
* Return Type: Boolean
* Description: creates a pixmap from the given image data
* In:
* html: XmHTMLWidget
* image: raw image data
* width: requested image width
* height: requested image height
* clip: clipmask, updated upon return.
* Returns:
* A pixmap upon success, None otherwise.
*****/
TPixmap
_XmHTMLInfoToPixmap(XmHTMLWidget html, XmHTMLImage *image,
XmImageInfo *info, Dimension width, Dimension height,
unsigned long *global_cmap, TPixmap *clip)
{
Display *dpy = Toolkit_Display((Widget)html);
TColormap cmap;
TWindow win;
unsigned long *color_map;
TXImage *ximage = NULL;
static TPixmap pixmap;
XCC xcc;
*clip = TNone;
/*
* external images are only scaled if the dimensions specified in the
*
attributes differ from the real image dimensions.
* The XmIMAGE_ALLOW_SCALE bit must also be set (true by default).
*/
if(!(ImageIsInternal(image)) && ImageInfoScale(info) &&
(height != info->height || width != info->width))
{
scaleImage(info, width, height);
image->height = height;
image->width = width;
}
/*
* get a window
* if we are realized we have a window
*/
if(Toolkit_Is_Realized((Widget)html))
win = (XmIsHTML((Widget)html) ? Toolkit_Widget_Window(html->html.work_area) :
Toolkit_Widget_Window((TWidget)html));
else
win = Toolkit_Default_Root_Window(dpy);
/* get colormap: every widget is derived from core so this is easy */
cmap = Toolkit_Widget_Colormap(html);
/*
* Set XCC for this image.
* When we are creating images that have nothing to do with XmHTML,
* the image already has a privatly owned XCC.
*/
if((xcc = image->xcc) == NULL)
{
if(XmIsHTML((Widget)html))
{
if(!html->html.xcc)
_XmHTMLCheckXCC(html);
image->xcc = html->html.xcc;
}
}
/* allocate colors used by the image and create it */
if(global_cmap)
{
color_map = global_cmap;
_XmHTMLDebug(6, ("image.c: _XmHTMLInfoToPixmap, using global "
"colormap.\n"));
}
else
{
_XmHTMLDebug(6, ("image.c: _XmHTMLInfoToPixmap, allocating local "
"colormap with %i entries\n", info->ncolors));
color_map = makeColormap(html, image, info);
}
if((ximage = _XmHTMLCreateXImage(html, xcc, info->width,
info->height, image->url)) != NULL)
_XmHTMLFillXImage(html, ximage, xcc, info->data, color_map, NULL, NULL);
if(color_map != global_cmap)
free(color_map);
if(ximage != NULL)
{
TGC gc;
/* create the pixmap */
#ifdef WITH_MOTIF
if((pixmap = Toolkit_Create_Pixmap(dpy, win, info->width, info->height,
image->xcc->visualInfo->depth)) == TNone)
#else
if((pixmap = Toolkit_Create_Pixmap(dpy, win, info->width, info->height,
image->xcc->visual->depth)) == TNone)
#endif
{
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLInfoToPixmap"),
"%s: failed to create Pixmap.", image->url ?
image->url : "(animation frame)");
Toolkit_Image_Destroy(ximage);
return(TNone);
}
/* copy the image into the pixmap */
#ifdef WITH_MOTIF
gc = XCreateGC(dpy, pixmap, 0, 0);
XSetFunction(dpy, gc, GXcopy);
XPutImage(dpy, pixmap, gc, ximage, 0, 0, 0, 0, info->width,
info->height);
XFreeGC(dpy, gc);
#else
gc = gdk_gc_new(pixmap);
gdk_gc_set_function(gc, GDK_COPY);
gdk_draw_image(pixmap, gc, ximage, 0, 0, 0, 0, info->width, info->height);
gdk_gc_destroy(gc);
#endif
Toolkit_Image_Destroy(ximage);
/* Create a clip mask if are to use one */
if(ImageInfoClipmask(info))
{
_XmHTMLDebug(6, ("images.c, _XmHTMLInfoToPixmap, "
"creating clip mask for %s\n", image->url));
#ifdef WITH_MOTIF
*clip = XCreatePixmapFromBitmapData(dpy, win,
(char*)info->clip, info->width, info->height, 1, 0, 1);
#else
{
GdkColor fg, bg;
fg.pixel = 1;
bg.pixel = 0;
*clip = gdk_pixmap_create_from_data(win, info->clip, info->width, info->height,
1, &fg, &bg);
}
#endif
#ifdef DEBUG
/* write out this bitmap */
if(XmIsHTML((Widget)html) && html->html.debug_save_clipmasks)
{
char xbm[1024];
static int num;
int i;
if(strstr(image->url, "/"))
{
for(i = strlen(image->url) - 1;
i > 0 && image->url[i] != '/'; i--);
sprintf(xbm, "%s.%i.xbm", &image->url[i+1], num);
}
else
sprintf(xbm, "%s.%i.xbm", image->url, num);
XWriteBitmapFile(dpy, xbm, (Pixmap) *clip, info->width, info->height,
0, 0);
fprintf(stderr, "Wrote clipping bitmap to file %s\n", xbm);
num++;
}
#endif
}
/* return the pixmap */
return(pixmap);
}
else
{
/* failed, can't create this image, it might be too large? */
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLInfoToPixmap"),
"XCreateImage failed for %s, ignoring this image.",
image->url ? image->url : "(animation frame)");
return(TNone);
}
}
/*****
* Name: _XmHTMLMakeAnimation
* Return Type: void
* Description: creates a series of image that form an animation
* In:
* html: XmHTMLWidget id
* image: animation data, updated upon return
* width: logical screen width
* height: logical screen height
* Returns:
* nothing, but image contains a series of pixmaps forming the animation.
*****/
void
_XmHTMLMakeAnimation(XmHTMLWidget html, XmHTMLImage *image, Dimension width,
Dimension height)
{
TPixmap pixmap, clip;
int i = 0, nframes = image->html_image->nframes;
Dimension w = width, h = height;
XmImageInfo *frame = image->html_image;
float width_p = 0., height_p = 0.;
unsigned long *global_cmap;
_XmHTMLDebug(6, ("images.c: _XmHTMLMakeAnimation Start, creating "
"animation %s\n", image->url));
image->options |= IMG_ISANIM;
image->nframes = nframes;
image->frames = (XmImageFrame*)calloc(nframes, sizeof(XmImageFrame));
image->html = html;
#ifdef WITH_MOTIF
image->context = XtWidgetToApplicationContext((TWidget)html);
#else
image->context = NULL; /* FIXME */
#endif
image->current_frame = 0;
/*****
* Animations can also be scaled. To do this, we scale each frame
* with the scale factor that needs to be applied to the logical screen
* area. The logical screen area is given by the width and height of the
* first frame.
*****/
height_p = (float)(height/(float)frame->height);
width_p = (float)(width/(float)frame->width);
_XmHTMLDebug(6, ("images.c: _XmHTMLMakeAnimation, global scaling factor:\n"
"\thorizontal: %f\n\tvertical: %f\n", height_p, width_p));
/* global colormap */
global_cmap = makeColormap(html, image, frame);
while(frame && i < nframes)
{
_XmHTMLDebug(6, ("images.c: _XmHTMLMakeAnimation, creating frame "
"%i\n", i));
/* use current frame dimensions instead of global image dimensions */
if(!(frame->options & XmIMAGE_FRAME_IGNORE))
{
w = (int)(width_p*frame->width);
h = (int)(height_p*frame->height);
/*****
* If the dimensions of this frame differ from the logical screen
* dimensions or a frame has a disposal method other than none, we
* run the animation on an internal pixmap and blit this pixmap to
* screen when all required processing for the current frame has
* been done. This is an enormous performance enhancment since we
* only need to do one screen update.
* Animations of which each frame has the same size and a disposal
* method of none are blit to screen directly as they require no
* processing whatsoever.
*****/
if((w != width || h != height ||
frame->dispose != XmIMAGE_DISPOSE_NONE) &&
!ImageHasState(image))
image->options |= IMG_HASSTATE;
/*****
* Only use local colormap if we've got one.
*****/
if((pixmap = _XmHTMLInfoToPixmap(html, image, frame, w, h,
i && frame->ncolors ? NULL : global_cmap, &clip)) == TNone)
{
image->html_image->nframes = i;
return;
}
image->frames[i].pixmap = pixmap;
image->frames[i].clip = clip;
}
else
image->frames[i].pixmap = TNone;
image->frames[i].x = (int)(width_p*(float)frame->x);
image->frames[i].y = (int)(height_p*(float)frame->y);
image->frames[i].w = w;
image->frames[i].h = h;
image->frames[i].dispose = frame->dispose;
image->npixels = 0;
/* adjust animation timeout if it's too small */
image->frames[i].timeout = (frame->timeout ? frame->timeout : 50);
/* move to next animation frame */
frame = frame->frame;
i++;
}
/* no longer needed */
free(global_cmap);
/*
* Fallback images when loop_count is used or animations are frozen
* The XmIsHTML test is required since these routines can also be used
* for creating images that are not part of XmHTML. These images don't
* have an animation state maintainer.
*/
if(XmIsHTML((Widget)html) && ImageHasState(image))
{
TWindow win = (html->html.gc == NULL ?
Toolkit_Default_Root_Window(dpy) : Toolkit_Widget_Window(html->html.work_area));
/* empty pixmap for current animation state */
#ifdef WITH_MOTIF
image->pixmap = Toolkit_Create_Pixmap(dpy, win, width, height,
html->html.xcc->visualInfo->depth);
#else
image->pixmap = Toolkit_Create_Pixmap(dpy, win, width, height,
html->html.xcc->visual->depth);
#endif
/* no clipmask for the current animation state */
/* first frame is first state of this animation */
if(html->html.gc != NULL)
Toolkit_Copy_Area(dpy, image->frames[0].pixmap, image->pixmap,
html->html.gc, 0, 0, width, height, 0, 0);
_XmHTMLDebug(6, ("images.c: _XmHTMLMakeAnimation, allocated a "
"state maintainer for this animation (%ix%i)\n", width, height));
}
else
{
/* fallback images when loop_count is used or animations are frozen */
image->pixmap = image->frames[0].pixmap;
image->clip = image->frames[0].clip;
}
#ifdef DEBUG
if(XmIsHTML((Widget)html) && html->html.debug_no_loopcount)
image->loop_count = 0;
else
#endif
image->loop_count = image->html_image->loop_count;
image->current_loop = 0;
image->current_frame = 0;
/* this will initialize looping in DrawImage, paint.c */
image->options |= IMG_FRAMEREFRESH;
_XmHTMLDebug(6, ("images.c: _XmHTMLMakeAnimation End\n"));
}
static XmImageInfo*
readGifAnimation(TWidget w, ImageBuffer *ib)
{
int width, height, x, y, screen_width, screen_height, dispose;
static XmImageInfo *all_frames;
XmImageInfo *frame;
int nframes = 0, loop_count = 0, timeout = 50, bg;
/* fallbacks if no dispose or timeout method is specified */
int fallback_dispose = XmIMAGE_DISPOSE_NONE, fallback_timeout = 50;
static XmHTMLRawImageData img_data, master;
int global_used[MAX_IMAGE_COLORS], global_size = 0;
_XmHTMLDebug(6, ("images.c, readGifAnimation, %s is an animated "
"gif\n", ib->file));
all_frames = frame = NULL;
memset(&global_used, 0, MAX_IMAGE_COLORS*sizeof(int));
/* initialize gif animation reading */
if((loop_count = _XmHTMLGifAnimInit(w, ib, &master)) == -1)
return(NULL);
screen_height = master.height;
screen_width = master.width;
bg = master.bg;
/* ignore loop count when debugging images has been enabled */
#ifdef DEBUG
if(xmhtml_debug_levels_defined[6])
loop_count = 0;
#endif
/* keep reading frames until we run out of them */
while(_XmHTMLGifAnimNextFrame(ib, &img_data, &x, &y,
&timeout, &dispose))
{
/* Go and create each frame. */
if(nframes)
{
int cnt = global_size;
frame->frame = animDefaultProc(w, &img_data, &master,
&global_used[0], &cnt, False, NULL);
frame = frame->frame;
/*****
* Check if the global colormap has been modified.
* We may *never* do this for images that have been quantized!
*****/
if(global_size < cnt && frame->ncolors == frame->scolors)
{
/* release previous colormap */
free(all_frames->reds);
/* store new cmap */
global_size = cnt;
all_frames->reds = frame->reds;
all_frames->greens = frame->greens;
all_frames->blues = frame->blues;
all_frames->ncolors = all_frames->scolors = global_size;
/* wipe colormap for this frame, it uses the global one */
frame->reds = frame->greens = frame->blues = (Dimension*)NULL;
frame->ncolors = frame->scolors = 0;
}
}
else
{
/* this is the first frame with the master colormap */
all_frames = animDefaultProc(w, &img_data, &master,
&global_used[0], &global_size, True, ib->file);
frame = all_frames;
}
if(dispose)
fallback_dispose = dispose;
if(timeout)
fallback_timeout = timeout;
/* save info for this frame */
frame->depth = ib->depth;
frame->x = x;
frame->y = y;
frame->width = width = img_data.width;
frame->height = height = img_data.height;
frame->dispose = fallback_dispose;
frame->timeout = fallback_timeout;
/*
* reset dispose to none if the dimensions of this frame equal
* the screen dimensions *and* if this image isn't transparent.
* This will give us a small performance enhancement since the
* frame refreshing routines don't have to restore the background
* or blit the previous screen state to the screen.
*/
if(width == screen_width && height == screen_height && x == 0 &&
y == 0 && bg == -1)
frame->dispose = XmIMAGE_DISPOSE_NONE;
/*
* final check before moving to the next frame: if this frame doesn't
* fit on the logical screen, clip the parts that lie outside the
* logical screen area. Images that fall completely outside the
* logical screen are just left the way they are.
*/
if(x + width > screen_width || y + height > screen_height)
{
int new_w = x + width > screen_width ? screen_width - x : width;
int new_h = y + height > screen_height ? screen_height - y : height;
if(x < screen_width && y < screen_height)
clipImage(frame, new_w, new_h);
else
frame->options |= XmIMAGE_FRAME_IGNORE;
}
nframes++;
}
/* free global colormap */
free(master.cmap);
/* terminate gif animation reading */
_XmHTMLGifAnimTerminate(ib);
if(all_frames)
{
_XmHTMLDebug(6, ("images.c, readGifAnimation, loaded animation %s, "
"no of frames: %i, loop_count: %i\n", ib->file, nframes,
loop_count));
all_frames->loop_count = loop_count;
/* nframes is total no of frames in this animation */
all_frames->nframes = nframes;
}
return(all_frames);
}
/*****
* Name: _XmHTMLImageFileToBuffer
* Return Type: ImageBuffer*
* Description: loads a file into a memory buffer
* In:
* file: file to load
* Returns:
* filled ImageBuffer.
*****/
ImageBuffer*
_XmHTMLImageFileToBuffer(String file)
{
FILE *fp;
static ImageBuffer *ib;
int size;
ib = NULL;
if((fp = fopen(file, "r")) == NULL)
{
perror(file);
return(NULL);
}
fseek(fp, 0L, SEEK_END);
size = ftell(fp);
/* sanity check */
if(size == 0)
return(NULL);
rewind(fp);
ib = (ImageBuffer*)malloc(sizeof(ImageBuffer));
ib->buffer = (Byte*)malloc(size*sizeof(Byte));
ib->size = size;
if((fread(ib->buffer, ib->size, 1, fp)) != 1)
{
perror(file);
fclose(fp);
free(ib->buffer);
free(ib);
return(NULL);
}
fclose(fp);
ib->file = strdup(file);
ib->curr_pos = ib->buffer;
ib->next = 0;
ib->may_free = True;
return(ib);
}
static XmHTMLImage*
copyHTMLImage(XmHTMLWidget html, XmHTMLImage *image, String attributes)
{
static XmHTMLImage *dest;
XmHTMLImage *tmp;
/*****
* If creation for this image should be delayed until it's needed, set
* the global creation flag.
*****/
if(ImageDelayedCreation(image))
html->html.delayed_creation = True;
/*****
* If this is an orphaned image, it is currently not being used
* and can thus be used without copying it or inserting it in the
* list (images are orphaned when a resource is changed that does not
* require a reload of images itself).
*****/
if(ImageIsOrphaned(image))
{
_XmHTMLDebug(6, ("images.c: copyHTMLImage, found an orphaned "
"image: %s!\n", image->url));
image->options &= ~IMG_ORPHANED;
#ifdef WITH_MOTIF
image->context = XtWidgetToApplicationContext((TWidget)html);
#else
image->context = NULL; /* FIXME */
#endif
image->html = html;
return(image);
}
dest = copyImage(image, attributes);
/* These are unique to an image. */
#ifdef WITH_MOTIF
dest->context = XtWidgetToApplicationContext((TWidget)html);
#else
image->context = NULL; /* FIXME */
#endif
dest->html = html;
_XmHTMLDebug(6, ("images.c: copyHTMLImage, image %s copied, "
"dimensions: %ix%i\n", dest->html_image->url, dest->swidth,
dest->sheight));
/* store in the image list */
addImageToList(html, dest);
/* add it to the child list of the parent image */
if(image->child == NULL)
image->child = dest;
else
{
for(tmp = image->child; tmp != NULL && tmp->child != NULL;
tmp = tmp->child);
tmp->child = dest;
}
return(dest);
}
/*****
* Name: initAlphaChannels
* Return Type: void
* Description: initializes alpha channel processing: obtains background
* color/image information which will be merged with
* alpha-channeled images.
* In:
* html: XmHTMLWidget id;
* for_body..: True when we should initialize for body image processing.
* Returns:
* nothing.
*****/
static void
initAlphaChannels(XmHTMLWidget html, Boolean for_body_image)
{
AlphaPtr alpha;
/*****
* Always (re-)initialize the AlphaChannel buffer. If the body image
* is an alpha-channeled image we must use the current background *color*
* for it, and for all subsequent images we need to use the colormap of
* the background *image*.
*****/
if(!html->html.alpha_buffer)
html->html.alpha_buffer = (AlphaPtr)malloc(sizeof(AlphaChannelInfo));
else if(html->html.alpha_buffer->ncolors)
free(html->html.alpha_buffer->bg_cmap);
alpha = html->html.alpha_buffer;
alpha->bg_cmap = (TColor*)NULL;
alpha->ncolors = 0;
#ifdef WITH_MOTIF
alpha->fb_maxsample = (1 << html->core.depth) - 1;
#else
alpha->fb_maxsample = (1 << gtk_widget_get_visual(GTK_WIDGET(html))->depth) - 1;
#endif
/* no body image or this *is* the body image, use background color */
if(html->html.body_image == NULL || for_body_image)
{
TColor bg_color;
/* current background color */
bg_color.pixel = html->html.body_bg;
/* get rgb components */
#ifdef WITH_MOTIF
XQueryColor(XtDisplay((Widget)html), html->core.colormap,
&bg_color);
#else
my_x_query_colors(Toolkit_Widget_Colormap(html), &bg_color, 1);
#endif
/* downscale to range 0-255, required for alpha channel processing */
alpha->background[0] = bg_color.red >> 8;
alpha->background[1] = bg_color.green >> 8;
alpha->background[2] = bg_color.blue >> 8;
}
else
{
int i;
unsigned long *color_map;
XmImageInfo *bg_image = html->html.body_image->html_image;
/* allocate color_map pixel entries */
color_map = (unsigned long*)calloc(bg_image->ncolors,
sizeof(unsigned long));
/*****
* Note:
* Due to the nature/smartness of the XCC, no color allocation will be
* performed when we make the call below: the body image is already
* loaded and the colors it uses have been allocated.
* XCCGetPixels either has a color lookup table or a hashtable
* of already allocated colors (depending on the current visual, but
* XCC takes care of all that), and since the body colors have
* been allocated by the time we make this call, we will simply
* get the pixels corresponding to already allocated colors.
*
* I *love* this, there are some *true* gems in this code of mine!
*****/
alpha->ncolors = 0;
XCCGetPixels(html->html.xcc, bg_image->reds, bg_image->greens,
bg_image->blues, bg_image->ncolors, color_map,
&alpha->ncolors);
/* use all pixel values, not only the allocated ones */
alpha->ncolors = bg_image->ncolors;
/* initialize body image colormap */
alpha->bg_cmap = (TColor*)calloc(alpha->ncolors, sizeof(TColor));
for(i = 0; i < alpha->ncolors; i++)
alpha->bg_cmap[i].pixel = color_map[i];
/* no longer needed */
free(color_map);
/* get rgb values */
#ifdef WITH_MOTIF
XQueryColors(Toolkit_Display((TWidget)html), html->core.colormap,
alpha->bg_cmap, alpha->ncolors);
#else
my_x_query_colors(Toolkit_Widget_Colormap(html), alpha->bg_cmap, alpha->ncolors);
#endif
/* downscale to range 0-255, required for alpha channel processing */
for(i = 0; i < alpha->ncolors ; i++)
{
alpha->bg_cmap[i].red >>= 8;
alpha->bg_cmap[i].green >>= 8;
alpha->bg_cmap[i].blue >>= 8;
}
}
}
/*****
* Name: doAlphaChannel
* Return Type: void
* Description: recreates an image specified by image_word
* In:
* html: XmHTMLWidget id;
* image_word: image data;
* Returns:
* nothing, but the image described by image_word contains new pixmaps.
* Note:
* This routine is only used for PNG images with either a tRNS chunk or an
* alpha channel. As the actual pixmap contents depend on the location of
* the image in the document, we need to create a new pixmap every time the
* document is resized.
*****/
static void
doAlphaChannel(XmHTMLWidget html, XmHTMLImage *image)
{
XmImageInfo *html_image = image->html_image;
XmImageInfo *new_info;
XmHTMLRawImageData *img_data = NULL, raw_data;
int x = 0, y = 0;
TPixmap pixmap, clip;
unsigned int flags = 0;
_XmHTMLDebug(6, ("doAlphaChannel, rereading image %s\n", image->url));
/*****
* Verify that this image has an owner, e.g., is not the body image.
* If it has an owner, get its position. If this *is* the body image,
* alpha channel processing is done against the current background color.
* (either set thru or the default background color)
*****/
if(image->owner)
{
x = image->owner->words[0].x;
y = image->owner->words[0].y;
}
/* we *require* an XmImageInfo structure */
if(ImageInfoFreed(image) || html_image == NULL)
{
_XmHTMLWarning(__WFUNC__(html, "doAlphaChannel"),
"Alpha channel processing failed for image\n %s:\n"
" No XmImageInfo attached.", image->url);
image->options &= ~IMG_DELAYED_CREATION;
return;
}
/* sanity */
if(html_image->type != IMAGE_PNG)
{
_XmHTMLWarning(__WFUNC__(html, "doAlphaChannel"),
"Alpha channel processing failed for image\n %s:\n"
" Image is not PNG (id = %i).", image->url,
(int)html_image->type);
image->options &= ~IMG_DELAYED_CREATION;
return;
}
/* more sanity */
if(!(ImageInfoDelayedCreation(html_image)))
{
_XmHTMLWarning(__WFUNC__(html, "doAlphaChannel"),
"Alpha channel processing failed for image\n %s:\n"
" XmIMAGE_DELAYED_CREATION bit not set.",
image->url);
image->options &= ~IMG_DELAYED_CREATION;
return;
}
if(CHECK_CALLBACK (html, anchor_track_callback, ANCHOR_TRACK))
{
char msg[1024];
XmHTMLAnchorCallbackStruct cbs;
(void)memset(&cbs, 0, sizeof(XmHTMLAnchorCallbackStruct));
sprintf(msg, "%s: processing alpha channel", image->url);
/* initialize callback fields */
cbs.reason = XmCR_HTML_ANCHORTRACK;
cbs.event = NULL;
cbs.url_type = ANCHOR_FILE_LOCAL; /* doesn't really matter */
cbs.line = 0;
cbs.href = msg;
cbs.target = NULL;
cbs.rel = NULL;
cbs.rev = NULL;
cbs.title = NULL;
cbs.doit = False; /* doesn't matter */
cbs.visited = False; /* doesn't matter */
Toolkit_Call_Callback ((TWidget)html, html->html.anchor_track_callback,
ANCHOR_TRACK, &cbs);
Toolkit_Flush (Toolkit_Display((TWidget)html), False);
#ifdef WITH_MOTIF
XmUpdateDisplay((TWidget)html);
#endif
}
/* fill in a rawImage structure */
raw_data.data = html_image->alpha;
raw_data.alpha = (Byte*)NULL;
raw_data.width = html_image->swidth;
raw_data.height = html_image->sheight;
raw_data.bg = -1;
raw_data.cmap = (TColor*)NULL;
raw_data.cmapsize = 0;
raw_data.type = html_image->type;
raw_data.fg_gamma = html_image->fg_gamma;
raw_data.color_class = html_image->colorspace;
raw_data.delayed_creation = True;
_XmHTMLDebug(6, ("doAlphaChannel, processing alpha channel.\n"));
img_data = _XmHTMLReReadPNG(html, &raw_data, x, y, image->owner == NULL);
_XmHTMLDebug(6, ("doAlphaChannel, processing read image data.\n"));
img_data->type = IMAGE_PNG;
new_info = imageDefaultProc((TWidget)html, img_data, image->url);
free(img_data);
/* sanity */
if(new_info == NULL)
{
image->options &= ~IMG_DELAYED_CREATION;
return;
}
/* update the XmImageInfo for this image */
if(!(ImageInfoShared(html_image)))
{
if(html_image->data)
free(html_image->data);
if(ImageInfoClipmask(html_image))
free(html_image->clip);
if(ImageInfoRGBSingle(html_image))
{
if(html_image->reds)
free(html_image->reds);
}
else
{
if(html_image->reds)
free(html_image->reds);
if(html_image->greens)
free(html_image->greens);
if(html_image->blues)
free(html_image->blues);
}
}
/*****
* Now copy members of the new ImageInfo structure that are likely to
* have changed. This is required if we do not want to mess up any of
* the user's XmImageInfo administration (caching and stuff).
* scolors is left untouched, unless it is zero (png has a palette size
* for gray and paletted colorspaces).
*****/
html_image->data = new_info->data;
html_image->clip = new_info->clip;
html_image->reds = new_info->reds;
html_image->greens = new_info->greens;
html_image->blues = new_info->blues;
html_image->depth = new_info->depth;
html_image->ncolors = new_info->ncolors;
html_image->depth = 8; /* always */
if(!html_image->scolors)
html_image->scolors = new_info->scolors;
/*****
* Check what flags we should set. We must check a number of flags
* that the user might have set himself (such as keeping the image alive).
* We ignore the delayed, clipmask and progressive flags: when we get
* here, the image is fully available. And alpha channels don't even
* begin to think about clipmasks, it's why I've got to deal with this
* delayed creation mess in the first place!!
*****/
if(ImageInfoFreeLater(html_image))
flags |= XmIMAGE_DEFERRED_FREE; /* free when doc switches? */
else if(ImageInfoFreeNow(html_image))
flags |= XmIMAGE_IMMEDIATE_FREE; /* free when image created? */
if(ImageInfoScale(html_image))
flags |= XmIMAGE_ALLOW_SCALE; /* may we scale? */
if(ImageInfoShared(html_image))
flags |= XmIMAGE_SHARED_DATA; /* must data be kept alive? */
/* flags from the imageDefaultProc and of course the delayed creation bit */
flags |= XmIMAGE_RGB_SINGLE|XmIMAGE_DELAYED_CREATION;
html_image->options = flags;
/* and free the new info, we no longer need it */
free(new_info);
/* this image has an alpha channel */
html_image->transparency = XmIMAGE_TRANSPARENCY_ALPHA;
/* save image type as well */
html_image->type = raw_data.type;
_XmHTMLDebug(6, ("doAlphaChannel, creating pixmap.\n"));
/*****
* Fourth step is to create the actual pixmap
* First destroy any previous pixmaps.
*****/
freePixmaps(html, image);
image->html_image = html_image;
pixmap = _XmHTMLInfoToPixmap(html, image, html_image, image->width,
image->height, NULL, &clip);
image->pixmap = pixmap;
image->clip = clip; /* which is always None */
/*****
* Unset delayed creation if we have a solid background color: the image
* does not have to be recreated when this doc is resized.
* Also unset delayed creation flag for the body image, it does not need
* reprocessing on document resize.
*****/
if(ImageIsBackground(image))
image->options &= ~IMG_DELAYED_CREATION;
else if(html->html.body_image)
image->options |= IMG_DELAYED_CREATION;
else
image->options &= ~IMG_DELAYED_CREATION;
/* and we are done */
_XmHTMLDebug(6, ("doAlphaChannel, done.\n"));
}
/*****
* Name: processBodyImage
* Return Type: void
* Description: final body image processing: verifies a few things and plugs
* in the background color if the body image is transparent.
* Stores the background image in the widget.
* In:
* html: XmHTMLWidget id;
* body_im..: actual background image;
* width: background image width;
* height: background image height;
* Returns:
* nothing.
*****/
static void
processBodyImage(XmHTMLWidget html, XmHTMLImage *body_image,
Dimension width, Dimension height)
{
/* animations are not allowed as background images */
if(ImageIsAnim(body_image))
{
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLLoadBodyImage"),
"animations not allowed as background images.");
html->html.body_image = NULL;
return;
}
/* mark as the background image */
body_image->options |= IMG_ISBACKGROUND;
/*
* See if the image was loaded succesfully, e.i. is an
* external image.
* might seem strange, but this image *will* be freed
* when XmHTMLFreeAllImages is called (e.i. destroy() is
* invoked or new text is set.
*/
if(ImageIsInternal(body_image))
html->html.body_image = NULL;
else
{
html->html.body_image = body_image;
/*****
* We support transparent background images as well.
* To do so, we need to create a pixmap, fill it with the
* current background color, set the clipmask and copy the
* real image onto it. Then we replace the old background image
* with the new semi-transparent background image.
*****/
if(!ImageDelayedCreation(body_image) && body_image->clip != TNone)
{
TWindow win;
TPixmap pixmap;
if(Toolkit_Widget_Is_Realized((TWidget)html))
win = Toolkit_Widget_Window(html->html.work_area);
else
win = Toolkit_Default_Root_Window(dpy);
/* create a new pixmap */
#ifdef WITH_MOTIF
if((pixmap = Toolkit_Create_Pixmap(dpy, win, width, height,
html->html.xcc->visualInfo->depth)) != TNone)
{
GC gc;
XGCValues values;
/* create background gc */
values.plane_mask = AllPlanes;
values.function = GXcopy;
gc = XCreateGC(dpy, win, GCFunction | GCPlaneMask, &values);
/* do a fillrect in the given background color */
XSetForeground(dpy, gc, html->html.body_bg);
XFillRectangle(dpy, pixmap, gc, 0, 0, width, height);
/* set clipmask */
values.clip_mask = body_image->clip;
values.clip_x_origin = 0;
values.clip_y_origin = 0;
XChangeGC(dpy, gc, GCClipMask | GCClipXOrigin | GCClipYOrigin,
&values);
#else
if((pixmap = Toolkit_Create_Pixmap(dpy, win, width, height,
html->html.xcc->visual->depth)) != TNone)
{
TGC gc;
TColor color;
gc = gdk_gc_new(win);
color.pixel = html->html.body_bg;
gdk_gc_set_foreground(gc, &color);
gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, width, height);
gdk_gc_set_clip_mask(gc, body_image->clip);
gdk_gc_set_clip_origin(gc, 0, 0);
#endif
/* and copy original pixmap to the new pixmap */
Toolkit_Copy_Area(dpy, body_image->pixmap, pixmap, gc, 0, 0,
width, height, 0, 0);
/* release original pixmap/gc */
FreePixmap(dpy, body_image->pixmap);
FreePixmap(dpy, body_image->clip);
/* release gc */
Toolkit_GC_Free(dpy, gc);
/* save new pixmap */
body_image->pixmap = pixmap;
body_image->clip = TNone;
/* all done! */
}
}
}
}
/********
****** Private XmHTML Functions
********/
/*****
* Name: _XmHTMLGetImageType
* Return Type: int
* Description: determines the type of an image
* In:
* ib: image for which to determine the type
* Returns:
* image type upon success, IMAGE_UNKNOWN on failure to determine
* the type.
*****/
Byte
_XmHTMLGetImageType(ImageBuffer *ib)
{
Byte ret_val = IMAGE_UNKNOWN;
Byte magic[30];
static Byte png_magic[8] = {137, 80, 78, 71, 13, 10, 26, 10};
(void)memcpy(magic, ib->buffer, 30);
/* check image types we known of. Do most (?) logical order */
if(!(strncmp((char*)magic, "GIF87a", 6)) ||
!(strncmp((char*)magic, "GIF89a", 6)))
ret_val = (Byte)_XmHTMLIsGifAnimated(ib);
/* compatible gif */
else if(!(strncmp((char*)magic, "GZF87a", 6)) ||
!(strncmp((char*)magic, "GZF89a", 6)))
{
ret_val = (Byte)_XmHTMLIsGifAnimated(ib);
/* and adjust return value */
if(ret_val == IMAGE_GIF)
ret_val = IMAGE_GZF;
else if(ret_val == IMAGE_GIFANIM)
ret_val = IMAGE_GZFANIM;
else ret_val = IMAGE_GZFANIMLOOP;
}
else if(!(strncmp((char*)magic, "eX!flg", 6)))
ret_val = IMAGE_FLG;
else if(magic[0] == 0xff && magic[1] == 0xd8 && magic[2] == 0xff)
ret_val = IMAGE_JPEG;
else if(!(memcmp(magic, png_magic, 8)))
ret_val = IMAGE_PNG;
else if(!(strncmp((char*)magic, "/* XPM */", 9)))
ret_val = IMAGE_XPM;
else if(!(strncmp((char*)magic, "#define", 7)) ||
(magic[0] == '/' && magic[1] == '*'))
ret_val = IMAGE_XBM;
return(ret_val);
}
/*****
* Name: _XmHTMLNewImage
* Return Type: XmHTMLImage
* Description: creates and fills an image structure
* In:
* html: XmHTMLWidget
* attributes: image source data
* *width: image width, updated upon return
* *height: image height, updated upon return
* Returns:
* a newly created image, all fields filled.
* Note:
* The width and height returned are stored in the owning ObjectTable
* element. The real image dimensions are stored in the XmImageInfo and
* XmHTMLImage structure. A resize of the image is only done if the
* real image dimensions differ from the requested dimensions and then only
* if the image isn't part of an animation or is an internal image.
*****/
XmHTMLImage*
_XmHTMLNewImage(XmHTMLWidget html, String attributes, Dimension *width,
Dimension *height)
{
static XmHTMLImage *image;
static XmImageInfo *html_image;
String src;
unsigned char image_type = 0;
TPixmap pixmap = TNone, clip = TNone;
/* no XmImage stuff, so reset it to NULL */
_xmimage_cfg = (XmImageConfig*)NULL;
/* See if we have a source for this image. If not, just return */
if((src = _XmHTMLTagGetValue(attributes, "src")) == NULL)
return(NULL);
/* get specified width and height */
*width = _XmHTMLTagGetNumber(attributes, "width", 0);
*height = _XmHTMLTagGetNumber(attributes, "height", 0);
if(html->html.images_enabled)
{
/*****
* We try to be a little bit carefull with system resources.
* HTML documents intend to have an increasing amount of images in
* them, so we try to reuse as much as possible.
* lookForImage also updates width and height if width and/or height
* is zero or one of them is zero and the other one matches the real
* image dimension.
* Images for which the delayed creation bit has been set cannot be
* copied as they will be different depending on their location on
* screen.
*****/
if((image = lookForImage(html, src, attributes, width, height)) != NULL)
{
free(src);
return(copyHTMLImage(html, image, attributes));
}
if(html->html.image_proc)
{
/* only external loaders can enable delayed image loading */
html_image = html->html.image_proc((TWidget)html, src,
html->html.client_data);
/*
* If this image is to be loaded progressively we *need* to have
* a get_data() function installed. It's an error if it's not!
*/
if(html_image && ImageInfoProgressive(html_image))
{
PLC *plc;
if(!XmIsHTML((TWidget)html))
_XmHTMLError(__WFUNC__(html, "_XmHTMLNewImage"),
"Fatal: can't do progressive image loading for "
"XmImage.");
if(html->html.get_data == NULL)
_XmHTMLError(__WFUNC__(html, "_XmHTMLNewImage"),
"Fatal: progressive image loading requires a "
"XmNprogressiveReadProc procedure!");
/* progressive images can't also be delayed */
if(ImageInfoDelayed(html_image))
html_image->options &= ~XmIMAGE_DELAYED;
/* no freeing of this ImageInfo */
html_image->options &= ~XmIMAGE_IMMEDIATE_FREE;
html_image->options &= ~XmIMAGE_DEFERRED_FREE;
/* create a context for this image */
plc = _XmHTMLPLCCreate(html, (XtPointer)html_image, src,
XmPLC_IMAGE);
/*
* Update/override PLC fields.
* This can include storing image-specific function pointers
* in the obj_funcs array of the returned PLC.
*
* The user_data field must *always* be set or there will be
* nothing to do. It is used as the user_data field in the
* stream argument to the get_data() and end_data() calls.
*/
plc->user_data = html_image->user_data;
}
/* see if loading of this image has been suspended */
if(html_image && ImageInfoDelayed(html_image))
{
XmImageInfo *delayed;
/* get delayed image information */
delayed = defaultImage(html, src, DEFAULT_IMG_SUSPENDED, False);
/* substitute appropriate data fields */
html_image->data = delayed->data;
html_image->clip = delayed->clip;
html_image->ncolors = delayed->ncolors;
html_image->reds = delayed->reds;
html_image->greens = delayed->greens;
html_image->blues = delayed->blues;
html_image->bg = delayed->bg;
html_image->depth = delayed->depth;
html_image->ncolors = html_image->scolors = delayed->ncolors;
html_image->colorspace = delayed->colorspace;
html_image->transparency = delayed->colorspace;
/* always use default image dimensions or bad things happen */
html_image->width = delayed->width;
html_image->height = delayed->height;
html_image->swidth = delayed->swidth;
html_image->sheight = delayed->sheight;
/* and we don't know the image type (yet) */
html_image->type = IMAGE_UNKNOWN;
/* need to append options of the default image as well */
html_image->options |= delayed->options;
image_type = IMG_ISINTERNAL;
}
}
else
html_image = XmHTMLImageDefaultProc((TWidget)html, src, NULL, 0);
/*
* This widget contains images which are to be created when they
* are needed.
*/
if(html_image && ImageInfoDelayedCreation(html_image))
html->html.delayed_creation = True;
}
else
{
/* see if we have this image already loaded */
if((image = lookForImage(html, src, attributes, width, height)) != NULL
&& ImageIsInternal(image))
return(copyHTMLImage(html, image, attributes));
/* we haven't got it, get a default image */
html_image = defaultImage(html, src, DEFAULT_IMG_SUSPENDED, False);
image_type = IMG_ISINTERNAL;
}
if(html_image == NULL)
{
/* imageProc failed, get default image */
html_image = defaultImage(html, src, DEFAULT_IMG_UNSUPPORTED, False);
image_type = IMG_ISINTERNAL;
/* This is *definitly* an error */
if(html_image == NULL)
{
_XmHTMLError(__WFUNC__(html, "_XmHTMLNewImage"),
"A fatal error occured in the default image loading "
"procedure for image\n %s\n", src);
}
}
/* allocate and initialize a new image */
image = (XmHTMLImage*)malloc(sizeof(XmHTMLImage));
(void)memset(image, 0, sizeof(XmHTMLImage));
image->magic = XmHTML_IMAGE_MAGIC;
image->html_image = html_image;
image->options = image_type;
/* store original url in private image info */
image->url = src;
/* Check if this image is an animation */
if(html_image->nframes > 1)
image->options |= IMG_ISANIM;
/* check if we have delayed creation */
if(ImageInfoDelayedCreation(html_image))
image->options |= IMG_DELAYED_CREATION;
/* store real image dimensions */
image->width = image->html_image->width;
image->height = image->html_image->height;
/* Store specified width and height (if any) */
if(*height && *width)
{
/* we have got dimensions */
image->options |= IMG_HASDIMENSIONS;
/* store requested dimensions */
image->sheight = *height;
image->swidth = *width;
/*****
* sanity check: if this is an internal image and the specified
* dimensions are smaller than default image dimensions, return the
* dimensions of the default image instead or text layout will be
* really horrible....
*****/
if(image_type == IMG_ISINTERNAL)
{
if(*height < image->height)
*height = image->height;
if(*width < image->width)
*width = image->width;
}
/* revert to original dimensions if we may not scale this image */
if(!ImageInfoScale(image->html_image))
{
*height = image->height;
*width = image->width;
}
}
else
{
image->swidth = *width = image->width;
image->sheight = *height = image->height;
}
/* store widget id */
image->html = html;
/* set XCC for this image */
if(!html->html.xcc)
_XmHTMLCheckXCC(html);
image->xcc = html->html.xcc;
/*
* Go and create the image.
* We have four options here:
* 1. the image can be an animation
* 2. the image can be a plain image;
* 3. the image can have the delayed creation flag set.
* 4. we are instructed to progressively load this image.
*/
if(ImageInfoProgressive(html_image))
{
_XmHTMLDebug(6, ("images.c: _XmHTMLNewImage, image %s:\n"
" Progressive bit set!\n", image->url));
if(ImageHasDimensions(image))
{
*width = image->swidth;
*height = image->sheight;
}
else /* we must set some return dimensions */
*width = *height = 64;
/* plc has already been created */
}
else if(ImageIsAnim(image))
_XmHTMLMakeAnimation(html, image, *width, *height);
else
{
if(!(ImageDelayedCreation(image)))
{
/* _XmHTMLInfoToPixmap will scale the image if required */
if((pixmap = _XmHTMLInfoToPixmap(html, image, html_image,
*width, *height, NULL, &clip)) == TNone)
{
_XmHTMLFreeImage(html, image);
return(NULL);
}
image->pixmap = pixmap;
image->clip = clip;
}
/* no additional processing for delayed image creation */
}
/* pick up remaining image attributes */
getImageAttributes(image, attributes);
_XmHTMLDebug(6, ("images.c: _XmHTMLNewImage, image %s loaded, "
"dimensions: %ix%i\n", image->url, image->width, image->height));
/* store in the image list */
addImageToList(html, image);
/* check if we may free the XmImageInfo right now */
if(ImageInfoFreeNow(html_image) && !ImageIsCopy(image))
{
_XmHTMLFreeImageInfo(html, html_image, False);
image->html_image = NULL;
}
return(image);
}
/*****
* Name: _XmHTMLImageUpdateChilds
* Return Type: void
* Description: updates all childs for the given parent image
* In:
* image: parent image data
* Returns:
* nothing
* Note:
* This routine udjusts the XmImageInfo and pixmap fields of an image
* child. It is called whenever _XmHTMLUpdateImage or _XmHTMLReplaceImage
* is called. It is also called from within plc.c at PLC initialization
* and at PLC completion.
*****/
void
_XmHTMLImageUpdateChilds(XmHTMLImage *image)
{
XmHTMLImage *tmp;
_XmHTMLDebug(6, ("images.c: _XmHTMLImageUpdateChilds, updating childs of "
"%s\n", image->url));
for(tmp = image->child; tmp != NULL; tmp = tmp->child)
{
/*
* update all necessary fields, *including* width and height
* of the word represented by this image!
*/
tmp->pixmap = image->pixmap;
tmp->clip = image->clip;
tmp->frames = image->frames;
tmp->nframes = image->nframes;
tmp->current_frame = image->current_frame;
tmp->current_loop = image->current_loop;
tmp->loop_count = image->loop_count;
tmp->options = image->options;
tmp->html_image = image->html_image;
tmp->context = image->context;
tmp->html = image->html;
tmp->width = image->width;
tmp->height = image->height;
tmp->swidth = image->swidth;
tmp->sheight = image->sheight;
/* and this is still a copy */
tmp->options |= IMG_ISCOPY;
/* update worddate for this image */
updateImageWord(tmp);
}
}
/*****
* Name: _XmHTMLReplaceOrUpdateImage
* Return Type: XmImageStatus
* Description: updates an image with new image data
* In:
* html: XmHTMLWidget
* info: existing image data to be updated. info must contain new
* image data
* new_info: new image info. If NULL the image is updated, else it's
* replaced.
* elePtr: element location, filled upon return when image dimensions
* are already known.
* Returns:
* XmIMAGE_OK if no layout recomputation is required, XmIMAGE_ALMOST if
* it is, an error code otherwise.
*****/
XmImageStatus
_XmHTMLReplaceOrUpdateImage(XmHTMLWidget html, XmImageInfo *info,
XmImageInfo *new_info, XmHTMLObjectTableElement *elePtr)
{
XmHTMLImage *image = NULL;
TPixmap pixmap = TNone, clip = TNone;
Boolean do_return = False;
int img_width, img_height;
_XmHTMLDebug(6, ("images.c: _XmHTMLReplaceOrUpdateImage, looking for %s\n",
info->url));
*elePtr = (XmHTMLObjectTableElement)NULL;
/* given image dimensions */
img_width = (new_info != NULL ? new_info->width : info->width);
img_height = (new_info != NULL ? new_info->height : info->height);
for(image = html->html.images; image != NULL; image = image->next)
{
_XmHTMLDebug(6, ("\tchecking %s\n", image->url));
if(image->html_image == info)
{
_XmHTMLDebug(6, ("images.c: _XmHTMLReplaceOrUpdateImage, "
"updating image %s\n", image->url));
/*
* get/set image dimensions.
* If dimensions have been specified on the original
spec,
* use those, else use the real image dimensions.
*/
if(ImageHasDimensions(image))
{
image->width = image->swidth;
image->height = image->sheight;
do_return = True;
}
else
{
/* image dimensions haven't changed: a true image reload */
if(image->width == img_width && image->height == img_height)
do_return = True;
image->swidth = image->width = img_width;
image->sheight = image->height = img_height;
}
/* if this is the background image, update body_image ptr */
if(ImageIsBackground(image))
html->html.body_image = image;
else
/*
* This image normally should have an owning object, so update
* the worddata for this image as well.
*/
updateImageWord(image);
/*
* if this image is a copy and it's no longer delayed,
* just return.
*/
if(ImageIsCopy(image) && !(ImageInfoDelayed(info)))
{
_XmHTMLDebug(6, ("images.c: _XmHTMLReplaceOrUpdateImage, "
"image update not required: current image is a copy\n"));
if(do_return && image->owner)
{
*elePtr = image->owner;
return(XmIMAGE_OK);
}
return(XmIMAGE_ALMOST);
}
/* check if we are replacing this image */
if(new_info != NULL)
{
/* release previous info */
if(!ImageIsInternal(image) &&
ImageInfoFreeLater(image->html_image))
_XmHTMLFreeImageInfo(html, image->html_image, False);
image->html_image = new_info;
}
/* free all pixmaps and allocated colors for this image */
freePixmaps(html, image);
/* no longer an internal image, nor a copy */
image->options &= ~(IMG_ISINTERNAL) & ~(IMG_ISCOPY);
/* just to be sure */
image->html_image->options &= ~(XmIMAGE_DELAYED);
image->html_image->options &= ~(XmIMAGE_SHARED_DATA);
/* if this image has delayed creation, don't let it be freed */
if(ImageInfoDelayedCreation(image->html_image))
{
/* may not be freed */
image->html_image->options &= ~(XmIMAGE_IMMEDIATE_FREE);
image->html_image->options &= ~(XmIMAGE_DEFERRED_FREE);
image->options |= IMG_DELAYED_CREATION;
html->html.delayed_creation = True;
}
/* Check if we are to create an animation or a plain image */
if(image->html_image->nframes > 1)
{
_XmHTMLMakeAnimation(html, image, image->width, image->height);
}
else
{
if(!(ImageDelayedCreation(image)))
{
/*
* Create the new pixmap. _XmHTMLInfoToPixmap will scale
* the image if the real image dimensions differ from the
* specified ones.
* This is a serious error if it fails.
*/
if((pixmap = _XmHTMLInfoToPixmap(html, image,
image->html_image, image->width, image->height, NULL,
&clip)) == TNone)
return(XmIMAGE_ERROR);
/* store it */
image->pixmap = pixmap;
image->clip = clip;
/* background image processing */
if(ImageIsBackground(image))
processBodyImage(html, image, image->width,
image->height);
}
else
{
/*****
* if the image dimensions are known, we can do alpha
* processing right away. Otherwise, we need to set the
* global delayed_creation flag which will trigger alpha
* processing when the user calls XmHTMLRedisplay after
* he has flushed all image.
*****/
if(do_return || ImageIsBackground(image))
{
initAlphaChannels(html, ImageIsBackground(image));
doAlphaChannel(html, image);
}
else
html->html.delayed_creation = True;
}
}
/* update all copies of this image */
_XmHTMLImageUpdateChilds(image);
/* return owner if image dimensions haven't changed */
if(do_return && image->owner)
{
*elePtr = image->owner;
return(XmIMAGE_OK);
}
return(XmIMAGE_ALMOST);
}
}
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLReplaceOrUpdateImage"),
"Can't update image %s.\n Provided XmImageInfo not bound to any "
"image.",
info->url);
return(XmIMAGE_UNKNOWN);
}
/*****
* Name: _XmHTMLFreeImage
* Return Type: void
* Description: Free private image data
* In:
* html: widget for obtaining display, required for freeing the pixmap
* image: image data to free
* Returns:
* nothing
* Note:
* The html_image member is only freed if the may_free member of that
* structure is set to true (which is automatically the case for images
* loaded by the imageDefaultProc).
*****/
void
_XmHTMLFreeImage(XmHTMLWidget html, XmHTMLImage *image)
{
/* sanity */
if(image == NULL)
{
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLFreeImage"),
"Attempt made to free a non-existing image!");
return;
}
/* always remove the animation timeout proc */
if(image->proc_id)
{
Toolkit_Timeout_Remove(image->proc_id);
image->proc_id = TNullTimeout;
}
/* see if this is image has been copied */
if(!ImageIsCopy(image) && !ImageInfoFreed(image))
{
_XmHTMLDebug(6, ("images.c: _XmHTMLFreeImage, freeing %s\n",
image->url ? image->url : ""));
/*
* See if we are allowed to free the XmImageInfo structure
* This is never the case for the internal images and *can* be
* the case for external images if the user has set the
* XmIMAGE_DEFERRED_FREE and/or XmIMAGE_IMMEDIATE_FREE bit.
*/
if(!ImageIsInternal(image) && image->html_image &&
(ImageInfoFreeNow(image->html_image) ||
ImageInfoFreeLater(image->html_image)))
_XmHTMLFreeImageInfo(html, image->html_image, False);
/*
* Free all pixmaps and allocated colors for this image.
* Also frees all XmImageFrame animation data.
*/
freePixmaps(html, image);
if(image->url)
free(image->url);
}
else
_XmHTMLDebug(6, ("images.c: _XmHTMLFreeImage, freeing a copy.\n"));
image->html_image = NULL;
/* free allocated strings */
free(image->alt);
if(image->map_url)
free(image->map_url);
free(image);
image = NULL;
}
/*****
* Name: _XmHTMLReleaseImage
* Return Type: void
* Description: releases the given image and updates the internal list
* of images.
* In:
* html: XmHTMLWidget id
* image: image to release
* Returns:
* nothing
*****/
void
_XmHTMLReleaseImage(XmHTMLWidget html, XmHTMLImage *image)
{
/* sanity */
if(image == NULL)
{
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLReleaseImage"),
"Internal Error: attempt to release a non-existing image.");
return;
}
/* first remove this image from the list of images */
if(image == html->html.images)
html->html.images = image->next;
else
{
XmHTMLImage *tmp = html->html.images;
/* walk the list until we find it */
for(; tmp->next != NULL && tmp->next != image; tmp = tmp->next);
/* not found */
if(tmp == NULL)
{
_XmHTMLWarning(__WFUNC__(html, "_XmHTMLReleaseImage"),
"Internal Error: image %s not found in allocated image list.",
image->url);
return;
}
/*
* Disconnect this image from the list.
* tmp is the image just before the image to release.
*/
tmp->next = image->next;
}
/* now release it */
_XmHTMLFreeImage(html, image);
}
/*****
* Name: _XmHTMLLoadBodyImage
* Return Type: void
* Description: loads the body image specified by the given url
* In:
* html: XmHTMLWidget id
* url: location of body image to load. When this is NULL, the current
* background image is removed.
* Returns:
* nothing.
*****/
void
_XmHTMLLoadBodyImage(XmHTMLWidget html, String url)
{
Dimension width, height;
String buf;
XmHTMLImage *body_image;
/* sanity check */
if(url == NULL)
{
html->html.body_image = NULL;
return;
}
_XmHTMLDebug(6, ("images.c: _XmHTMLLoadBodyImage, body image url is %s\n",
url));
/* kludge so _XmHTMLNewImage recognizes it */
buf = malloc(strlen(url)+7);
sprintf(buf, "src=\"%s\"", url);
/* load it */
if((body_image = _XmHTMLNewImage(html, buf, &width, &height)) != NULL)
processBodyImage(html, body_image, width, height);
free(buf);
}
/*****
* Name: _XmHTMLFreeImageInfo
* Return Type: void
* Description: free the given XmImageInfo structure
* In:
* html: XmHTMLWidget id;
* info: image to free;
* external: True when application code is freeing imageInfo, false if not.
* Returns:
* nothing
*****/
void
_XmHTMLFreeImageInfo(XmHTMLWidget html, XmImageInfo *info, Boolean external)
{
XmImageInfo *tmp;
XmHTMLImage *image;
_XmHTMLDebug(6, ("images.c: _XmHTMLFreeImageInfo Start, freeing %s\n",
(info->url ? info->url : "")));
/*****
* always set the info free bit, even if we are being called from
* application code.
*****/
if(XmIsHTML((TWidget)html)) /* fix 09/01/97-01, rr */
{
for(image = html->html.images; image != NULL; image = image->next)
{
if(image->html_image == info)
image->options |= IMG_INFOFREED;
}
}
/* also free all animation frames for this image */
while(info != NULL)
{
tmp = info->frame;
/* always free this */
if(info->url)
free(info->url);
info->url = NULL;
/*
* This will be true only for internal images of which the data
* may *never* be freed. Internal images are created only *once* and
* are used by every document that needs to display an internal image.
* Fix thanks to Dick Porter
*/
if(!(ImageInfoShared(info)))
{
if(info->data)
free(info->data);
if(ImageInfoClipmask(info))
free(info->clip);
if(ImageInfoRGBSingle(info))
{
if(info->reds)
free(info->reds);
}
else
{
if(info->reds)
free(info->reds);
if(info->greens)
free(info->greens);
if(info->blues)
free(info->blues);
}
if(ImageInfoDelayedCreation(info) && info->alpha)
free(info->alpha);
free(info);
}
/* and reset the ptr. */
info = NULL;
info = tmp;
}
_XmHTMLDebug(6, ("images.c: _XmHTMLFreeImageInfo End\n"));
}
/*****
* Name: _XmHTMLImageCheckDelayedCreation
* Return Type: void
* Description: walks the list of images and rereads them in order to process
* any alpha channel information.
* In:
* html: XmHTMLWidget id;
* Returns:
* nothing, but this routine unsets the delayed_creation flag if the
* current document does not have a body image (when alpha channel info
* is processed against a solid background color, no reprocessing is required
* since the position if the image does not matter).
* Note:
* It's the caller's responsibility to check whether or not the
* delayed_creation flag has been set.
*****/
void
_XmHTMLImageCheckDelayedCreation(XmHTMLWidget html)
{
XmHTMLImage *tmp;
Boolean for_body_image = False;
/* if we have a body image, but it's not yet available, do nothing */
if(html->html.body_image && !ImageDelayedCreation(html->html.body_image) &&
!BodyImageLoaded(html->html.body_image->html_image))
return;
/*****
* First check if the body image is present. If it is and we should
* process it now, the alphaChannel info must be initialized for background
* image alpha processing.
*****/
if(html->html.body_image && ImageDelayedCreation(html->html.body_image))
for_body_image = True;
/*****
* Always (re-)initialize the AlphaChannel buffer. If the body image
* is an alpha-channeled image we must use the current background *color*
* for it, and for all subsequent images we need to use the colormap of
* the background *image*.
*****/
initAlphaChannels(html, for_body_image);
/* now walk all images */
for(tmp = html->html.images; tmp != NULL; tmp = tmp->next)
{
if(ImageDelayedCreation(tmp))
{
doAlphaChannel(html, tmp);
/*****
* if this was the body image, we need to re-initialize the
* alphaChannel info, or images will use the background color
* instead of the new background image. doAlphaChannel will reset
* the delayed creation bit for alpha channelled images
* automatically, so we don't need to do this here.
* Note:
* You might wonder why we don't reprocess the images when
* we find out the background image has an alpha channel.
* We can get away with this 'cause the background image
* is *always* the first element in the list of images.
*****/
if(ImageIsBackground(tmp) && for_body_image)
initAlphaChannels(html, (for_body_image = False));
}
}
/*****
* If we don't have a body image, all images have been processed and
* do not need to be reprocessed when the document resizes.
*****/
if(html->html.body_image == NULL)
html->html.delayed_creation = False;
}
/********
****** Public XmHTML Functions
********/
/*****
* Name: XmHTMLImageDefaultProc
* Return Type: XmImageInfo*
* Description: XmHTML default image loading procedure
* In:
* w: Widget ID
* file: full name and location of image to load
* *buf: image data
* size: length of buf.
* Returns:
* An XmImageInfo structure on success or NULL on failure
*****/
XmImageInfo*
XmHTMLImageDefaultProc(TWidget w, String file, unsigned char *buf, int size)
{
ImageBuffer data, *ib;
static XmImageInfo *image;
image = NULL;
/* must be a valid filename */
if(!file)
return(NULL);
if(size == 0)
{
if((ib = _XmHTMLImageFileToBuffer(file)) == NULL)
return(NULL);
}
else
{
data.file = file;
data.buffer = (Byte*)buf;
data.size = (size_t)size;
data.next = 0;
data.may_free = False;
ib = &data;
}
/* assume all images have an initial depth of 8 bits */
ib->depth = 8;
_XmHTMLDebug(6, ("images.c, XmHTMLImageDefaultProc, checking type of "
"image %s\n", file));
ib->type = _XmHTMLGetImageType(ib);
/* sanity */
RewindImageBuffer(ib);
if(ib->type == IMAGE_UNKNOWN || ib->type == IMAGE_ERROR)
{
_XmHTMLDebug(6, ("images.c, XmHTMLImageDefaultProc, could not "
"determine type of image %s\n", file));
FreeImageBuffer(ib);
return(NULL);
}
switch(ib->type)
{
case IMAGE_GIFANIM:
case IMAGE_GIFANIMLOOP:
case IMAGE_GZFANIM:
case IMAGE_GZFANIMLOOP:
image = readGifAnimation(w, ib);
break;
case IMAGE_FLG:
/* bypasses the readImage + defaultImage proc entirely */
image = _XmHTMLReadFLG((XmHTMLWidget)w, ib);
break;
case IMAGE_XPM:
case IMAGE_XBM:
case IMAGE_GIF:
case IMAGE_GZF:
case IMAGE_JPEG:
case IMAGE_PNG:
{
XmHTMLRawImageData *img_data;
/* Go and load the given image */
if((img_data = readImage(w, ib)) != NULL)
{
/*****
* If image creation hasn't been delayed (it can be delayed
* if for example, a png image with an alpha channel or a
* tRNS chunk is being processed), use the default image
* proc to create an XmImageInfo structure for this image
*****/
if(img_data->delayed_creation == False)
image = imageDefaultProc(w, img_data, file);
else
image = imageDelayedProc(w, img_data, ib);
/*****
* No longer needed, destroy. Don't free the members of
* this structure as they are moved to the returned image.
*****/
free(img_data);
}
}
break;
default:
break;
}
if(image)
{
image->type = ib->type;
image->depth= ib->depth;
}
FreeImageBuffer(ib);
return(image);
}
/*****
* Name: ImageGetInfoSize
* Return Type: int
* Description: returns the size of the given XmImageInfo structure.
* In:
* info: ptr to a XmImageInfo structure;
* Returns:
* size of the given XmImageInfo structure.
* Note:
* This function is used both by us and the caching routines.
*****/
int
XmHTMLImageGetImageInfoSize(XmImageInfo *info)
{
int size = 0;
XmImageInfo *frame = info;
while(frame != NULL)
{
size += sizeof(XmImageInfo);
size += frame->width*frame->height; /* raw image data */
/* clipmask size. The clipmask is a bitmap of depth 1 */
if(frame->clip)
{
int clipsize;
clipsize = frame->width;
/* make it byte-aligned */
while((clipsize % 8))
clipsize++;
/* this many bytes on a row */
clipsize /= 8;
/* and this many rows */
clipsize *= frame->height;
size += clipsize;
}
/* reds, greens and blues */
size += 3*frame->ncolors*sizeof(Dimension);
frame = frame->frame; /* next frame of this image (if any) */
}
return(size);
}
/*****
* Name: XmHTMLImageFreeAllImages
* Return Type: void
* Description: releases all allocated images and associated structures
* In:
* html: XmHTMLWidget for which to free all images
* Returns:
* nothing
*****/
void
XmHTMLImageFreeAllImages(TWidget w)
{
XmHTMLImage *image, *image_list;
Display *dpy;
XmHTMLWidget html;
/* sanity check */
if(!w || !XmIsHTML(w))
{
_XmHTMLBadParent(w, "XmHTMLImageFreeAllImages");
return;
}
html = XmHTML (w);
image_list = html->html.images;
dpy = Toolkit_Display(w);
while(image_list != NULL)
{
image = image_list->next;
_XmHTMLFreeImage(html, image_list);
image_list = NULL;
image_list = image;
}
html->html.images = NULL;
/* alpha channel stuff */
if(html->html.alpha_buffer)
{
if(html->html.alpha_buffer->ncolors)
free(html->html.alpha_buffer->bg_cmap);
free(html->html.alpha_buffer);
}
html->html.alpha_buffer = (AlphaPtr)NULL;
/* only release XCC when we aren't using a fixed palette */
if(html->html.map_to_palette == XmDISABLED)
{
XCCFree(html->html.xcc);
html->html.xcc = (XCC)NULL;
}
}
/*****
* Name: XmHTMLImageFreeImageInfo
* Return Type: void
* Description: free the given XmImageInfo structure
* In:
* info: image to free
* Returns:
* nothing
*****/
void
XmHTMLImageFreeImageInfo(TWidget w, XmImageInfo *info)
{
static String func = "XmHTMLImageFreeImageInfo";
/* sanity check */
if(!w || !XmIsHTML(w))
{
_XmHTMLBadParent(w, func);
return;
}
/* sanity check */
if(info == NULL)
{
_XmHTMLWarning(__WFUNC__(NULL, func), "NULL XmImageInfo "
"structure passed to %s.", func);
return;
}
_XmHTMLFreeImageInfo((XmHTMLWidget)w, info, True);
}
/*****
* Name: XmHTMLImageAddImageMap
* Return Type: void
* Description: add the given imagemap to a HTML widget
* In:
* w: widget
* image_map: raw html data containing the imagemap to parse.
* Returns:
* nothing
*****/
void
XmHTMLImageAddImageMap(TWidget w, String image_map)
{
XmHTMLWidget html;
XmHTMLObject *parsed_map, *temp;
XmHTMLImageMap *imageMap = NULL;
/* sanity check */
if(!w || !XmIsHTML(w) || image_map == NULL)
{
_XmHTMLWarning(__WFUNC__(w, "XmHTMLImageAddImageMap"),
"%s passed to XmHTMLImageAddImageMap.",
w ? (image_map ? "Invalid parent" : "NULL imagemap") :
"NULL parent");
return;
}
html = XmHTML (w);
/* parse the imagemap */
if((parsed_map = _XmHTMLparseHTML(html, NULL, image_map, NULL)) == NULL)
return;
for(temp = parsed_map; temp != NULL; temp = temp->next)
{
switch(temp->id)
{
case HT_MAP:
if(temp->is_end)
{
_XmHTMLStoreImagemap(html, imageMap);
imageMap = NULL;
}
else
{
String chPtr;
chPtr = _XmHTMLTagGetValue(temp->attributes, "name");
if(chPtr != NULL)
{
imageMap = _XmHTMLCreateImagemap(chPtr);
free(chPtr);
}
else
_XmHTMLWarning(__WFUNC__(w, "XmHTMLAddImagemap"),
"unnamed map, ignored (line %i in input).",
temp->line);
}
break;
case HT_AREA:
if(imageMap)
_XmHTMLAddAreaToMap(html, imageMap, temp);
else
_XmHTMLWarning(__WFUNC__(w, "XmHTMLAddImagemap"),
" element outside