morse%netscape.com c62bb3e4a4 backout last checkin since it was breaking other platforms
git-svn-id: svn://10.0.0.236/trunk@63463 18797224-902f-48f8-a5cc-f745e15eee43
2000-03-20 18:44:06 +00:00

2431 lines
77 KiB
C++

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/* -*- Mode: C; tab-width: 4 -*-
* if.c --- Top-level image library routines
*/
#include "if.h"
#include "merrors.h"
#include "nsIImgDecoder.h"
#include "nsImgDCallbk.h"
#include "nsIComponentManager.h"
#include "xpcompat.h" //temporary, for timers
#include "prtypes.h"
#include "prprf.h"
#include "nsCRT.h"
#include "nsIPresContext.h"
#include "nsICookieService.h"
#include "nsIServiceManager.h"
PR_BEGIN_EXTERN_C
extern int XP_MSG_IMAGE_PIXELS;
extern int MK_UNABLE_TO_LOCATE_FILE;
extern int MK_OUT_OF_MEMORY;
PR_END_EXTERN_C
#define HOWMANY(x, r) (((x) + ((r) - 1)) / (r))
#define ROUNDUP(x, r) (HOWMANY(x, r) * (r))
int il_debug=0;
/* Global list of image group contexts. */
static IL_GroupContext *il_global_img_cx_list = NULL;
static NS_DEFINE_IID(kCookieServiceCID, NS_COOKIESERVICE_CID);
/*-----------------------------------------*/
NS_IMETHODIMP ImgDCallbk::ImgDCBSetupColorspaceConverter()
{
PRBool ret=PR_FALSE;
if( ilContainer != NULL ) {
ret = il_setup_color_space_converter(ilContainer);
}
if(ret)
return NS_OK;
else
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
ImgDCallbk::ImgDCBCreateGreyScaleColorSpace()
{
PRBool ret=PR_FALSE;
if( ilContainer != NULL ) {
NI_PixmapHeader *maskhdr = &ilContainer->mask->header;
ret = IL_CreateGreyScaleColorSpace(1,1, &(maskhdr->color_space));
}
if(ret)
return NS_OK;
else
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
ImgDCallbk::ImgDCBResetPalette()
{
PRBool ret=PR_FALSE;
if( ilContainer != NULL ) {
ret = il_reset_palette(ilContainer);
}
if(ret)
return NS_OK;
else
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
ImgDCallbk::ImgDCBHaveHdr(int destwidth, int destheight)
{
if( ilContainer != NULL ) {
il_dimensions_notify(ilContainer, destwidth, destheight);
}
return NS_OK; //no mem allocated here.
}
NS_IMETHODIMP
ImgDCallbk::ImgDCBInitTransparentPixel()
{
PRBool ret=PR_FALSE;
if( ilContainer != NULL ) {
ret = il_init_image_transparent_pixel(ilContainer);
}
if(ret)
return NS_OK;
else
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
ImgDCallbk::ImgDCBDestroyTransparentPixel()
{
if( ilContainer != NULL ) {
il_destroy_image_transparent_pixel(ilContainer);
}
return NS_OK; //no mem allocated here.
}
NS_IMETHODIMP
ImgDCallbk :: ImgDCBHaveRow(PRUint8 *rowbuf, PRUint8* rgbrow,
int x_offset, int len,
int row, int dup_rowcnt,
PRUint8 draw_mode,
int pass )
{
PRBool ret=PR_FALSE;
if( ilContainer != NULL ) {
ret = il_emit_row(ilContainer, rowbuf, rgbrow, x_offset, len,
row, dup_rowcnt, (il_draw_mode)draw_mode, pass );
}
if(ret)
return NS_OK;
else
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
ImgDCallbk :: ImgDCBHaveImageFrame()
{
if( ilContainer != NULL ) {
il_frame_complete_notify(ilContainer);
}
return NS_OK; //no mem allocated here.
}
NS_IMETHODIMP
ImgDCallbk::ImgDCBFlushImage()
{
if( ilContainer != NULL ) {
il_flush_image_data(ilContainer);
}
return NS_OK;
}
NS_IMETHODIMP
ImgDCallbk:: ImgDCBImageSize()
{
int ret = 0;
if( ilContainer != NULL ) {
ret = il_size(ilContainer); //still uses old imglib error msgs
// ret!=0 indicates error.
}
if(ret == 0)
return NS_OK;
else
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
ImgDCallbk :: ImgDCBHaveImageAll()
{
if( ilContainer != NULL ) {
il_image_complete(ilContainer);
}
return NS_OK;
}
NS_IMETHODIMP
ImgDCallbk :: ImgDCBError()
{
if( ilContainer != NULL ) {
//implement me
}
return NS_OK; //no mem allocated here.
}
void*
ImgDCallbk :: ImgDCBSetTimeout(TimeoutCallbackFunction func, void* closure, PRUint32 msecs)
{
return( IL_SetTimeout(func, closure, msecs ));
}
NS_IMETHODIMP
ImgDCallbk :: ImgDCBClearTimeout(void *timer_id)
{
IL_ClearTimeout(timer_id);
return NS_OK;
}
/*********************** Image Observer Notification. *************************
*
* These functions are used to send messages to registered observers of an
* individual image client i.e. an IL_ImageReq.
*
******************************************************************************/
/* Fabricate a descriptive title for the image and notify observers. Used for
calls to the image viewer. */
static void
il_description_notify(il_container *ic)
{
char buf[36];
IL_MessageData message_data;
IL_ImageReq *image_req;
NI_PixmapHeader *img_header = &ic->image->header;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
PR_snprintf(buf, sizeof(buf), XP_GetString(XP_MSG_IMAGE_PIXELS), ic->type,
img_header->width, img_header->height);
PR_ASSERT(ic->clients);
for (image_req = ic->clients; image_req; image_req = image_req->next) {
if (image_req->is_view_image) {
message_data.image_instance = image_req;
message_data.description = buf; /* Note scope limitation. Observer
must copy if necessary. */
XP_NotifyObservers(image_req->obs_list, IL_DESCRIPTION, &message_data);
}
}
}
/* Notify observers of the target dimensions of the image. */
void
il_dimensions_notify(il_container *ic, int dest_width, int dest_height)
{
IL_MessageData message_data;
IL_ImageReq *image_req;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
PR_ASSERT(ic->clients);
for (image_req = ic->clients; image_req; image_req = image_req->next) {
message_data.image_instance = image_req;
message_data.width = dest_width; /* Note: these are stored as */
message_data.height = dest_height; /* PRUint16s. */
XP_NotifyObservers(image_req->obs_list, IL_DIMENSIONS, &message_data);
}
}
/* Notify observers that the image is transparent and has a mask. */
static void
il_transparent_notify(il_container *ic)
{
IL_MessageData message_data;
IL_ImageReq *image_req;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
PR_ASSERT(ic->clients);
for (image_req = ic->clients; image_req; image_req = image_req->next) {
message_data.image_instance = image_req;
XP_NotifyObservers(image_req->obs_list, IL_IS_TRANSPARENT,
&message_data);
}
}
/* Notify observers that the image pixmap has been updated. */
void
il_pixmap_update_notify(il_container *ic)
{
IL_MessageData message_data;
IL_Rect *update_rect = &message_data.update_rect;
IL_ImageReq *image_req;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
update_rect->x_origin = 0;
update_rect->y_origin = (PRUint16)ic->update_start_row;
update_rect->width = (PRUint16)ic->image->header.width;
update_rect->height = (PRUint16)(ic->update_end_row-ic->update_start_row+1);
PR_ASSERT(ic->clients);
for(image_req = ic->clients; image_req; image_req = image_req->next) {
/* The user aborted the image loading. Don't display any more image
data */
if (image_req->stopped)
continue;
message_data.image_instance = image_req;
XP_NotifyObservers(image_req->obs_list, IL_PIXMAP_UPDATE,
&message_data);
}
}
/* Notify observers that the image has finished decoding. */
void
il_image_complete_notify(il_container *ic)
{
IL_MessageData message_data;
IL_ImageReq *image_req;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
for (image_req = ic->clients; image_req; image_req = image_req->next) {
message_data.image_instance = image_req;
XP_NotifyObservers(image_req->obs_list, IL_IMAGE_COMPLETE,
&message_data);
}
}
/* Notify observers that a frame of an image animation has finished
decoding. */
void
il_frame_complete_notify(il_container *ic)
{
IL_MessageData message_data;
IL_ImageReq *image_req;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
for (image_req = ic->clients; image_req; image_req = image_req->next) {
message_data.image_instance = image_req;
XP_NotifyObservers(image_req->obs_list, IL_FRAME_COMPLETE,
&message_data);
}
}
int
il_compute_percentage_complete(int row, il_container *ic)
{
PRUintn percent_height;
int percent_done = 0;
percent_height = (PRUintn)(row * (PRUint32)100 / ic->image->header.height);
switch(ic->pass) {
case 0: percent_done = percent_height; /* non-interlaced GIF */
break;
case 1: percent_done = percent_height / 8;
break;
case 2: percent_done = 12 + percent_height / 8;
break;
case 3: percent_done = 25 + percent_height / 4;
break;
case 4: percent_done = 50 + percent_height / 2;
break;
default:
ILTRACE(0,("Illegal interlace pass"));
break;
}
return percent_done;
}
/* Notify observers of image progress. */
void
il_progress_notify(il_container *ic)
{
PRUintn percent_done;
static PRUintn last_percent_done = 0;
int row = ic->update_end_row;
IL_MessageData message_data;
IL_ImageReq *image_req;
NI_PixmapHeader *img_header = &ic->image->header;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
/* No more progress bars for GIF animations after initial load. */
if (ic->is_looping)
return;
/* Calculate the percentage of image decoded (not displayed) */
if(ic->content_length) {
percent_done =
(PRUint32)100 * ic->bytes_consumed / ((PRUint32)ic->content_length);
/* Some protocols, e.g. gopher, don't support content-length, so
* show the percentage of the image displayed instead
*/
} else {
/* Could be zero if before il_size() is called. */
if (img_header->height == 0)
return;
int ret;
/* Interlaced GIFs are weird */
if ((ret = nsCRT::strncasecmp(ic->type, "image/gif", 9)) == 0) {
percent_done = il_compute_percentage_complete(row, ic);
}
else
{
/* This isn't correct for progressive JPEGs, but there's
* really nothing we can do about that because the number
* of scans in a progressive JPEG isn't known until the
* whole file has been read.
*/
percent_done = (PRUintn)(row * (PRUint32)100 / img_header->height);
}
}
if (percent_done != last_percent_done) {
PR_ASSERT(ic->clients);
for (image_req = ic->clients; image_req; image_req = image_req->next) {
if (image_req->is_view_image) { /* XXXM12N Eliminate this test? */
message_data.image_instance = image_req;
message_data.percent_progress = percent_done;
XP_NotifyObservers(image_req->obs_list, IL_PROGRESS,
&message_data);
}
}
last_percent_done = percent_done;
}
}
/* Notify observers of information pertaining to a cached image. Note that
this notification is per image request and not per image container. */
void
il_cache_return_notify(IL_ImageReq *image_req)
{
IL_MessageData message_data;
il_container *ic = image_req->ic;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
message_data.image_instance = image_req;
/* This function should only be called if the dimensions are known. */
PR_ASSERT(ic->state >= IC_SIZED);
/* First notify observers of the image dimensions. */
message_data.width = (unsigned short) ic->dest_width; /* Note: these are stored as */
message_data.height = (unsigned short) ic->dest_height; /* PRUint16s. */
XP_NotifyObservers(image_req->obs_list, IL_DIMENSIONS, &message_data);
message_data.width = message_data.height = 0;
/* Fabricate a title for the image and notify observers. Image Plugins
will need to supply information on the image type. */
il_description_notify(ic);
/* If the image is transparent and has a mask, notify observers
accordingly. */
if (ic->mask)
XP_NotifyObservers(image_req->obs_list, IL_IS_TRANSPARENT,
&message_data);
/* Now send observers a pixmap update notification for the displayable
area of the image. */
nsCRT::memmove(&message_data.update_rect, &ic->displayable_rect,
sizeof(IL_Rect));
XP_NotifyObservers(image_req->obs_list, IL_PIXMAP_UPDATE, &message_data);
nsCRT::zero(&message_data.update_rect, sizeof(IL_Rect));
if (ic->state == IC_COMPLETE) {
/* Send the observers a frame complete message. */
XP_NotifyObservers(image_req->obs_list, IL_FRAME_COMPLETE,
&message_data);
/* Finally send the observers a complete message. */
XP_NotifyObservers(image_req->obs_list, IL_IMAGE_COMPLETE,
&message_data);
}
}
/* Notify observers that the image request is being destroyed. This
provides an opportunity for observers to remove themselves from the
observer callback list and to do any related cleanup. */
void
il_image_destroyed_notify(IL_ImageReq *image_req)
{
IL_MessageData message_data;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
message_data.image_instance = image_req;
XP_NotifyObservers(image_req->obs_list, IL_IMAGE_DESTROYED, &message_data);
/* Now destroy the observer list itself. */
XP_DisposeObserverList(image_req->obs_list);
}
/* Notify observers that an error has occured. The observer should call
IL_DisplaySubImage in order to display the icon. Note that this function
is called per image request and not per image. */
static void
il_icon_notify(IL_ImageReq *image_req, int icon_number,
XP_ObservableMsg message)
{
IL_GroupContext *img_cx = image_req->img_cx;
int icon_width, icon_height;
IL_MessageData message_data;
nsCRT::zero(&message_data, sizeof(IL_MessageData));
/* Obtain the dimensions of the icon. */
img_cx->img_cb->GetIconDimensions(img_cx->dpy_cx, &icon_width,
&icon_height, icon_number);
/* Fill in the message data and notify observers. */
message_data.image_instance = image_req;
message_data.icon_number = icon_number;
message_data.icon_width = icon_width;
message_data.icon_height = icon_height;
XP_NotifyObservers(image_req->obs_list, message, &message_data);
}
/********************* Image Group Observer Notification. *********************
*
* This function is used to send messages to registered observers of an image
* group i.e. an IL_GroupContext.
*
******************************************************************************/
/* Notify observers that images have started/stopped loading in the context,
or started/stopped looping in the context. */
void
il_group_notify(IL_GroupContext *img_cx, XP_ObservableMsg message)
{
IL_GroupMessageData message_data;
nsCRT::zero(&message_data, sizeof(IL_GroupMessageData));
/* Fill in the message data and notify observers. */
message_data.display_context = img_cx->dpy_cx;
message_data.image_context = img_cx;
XP_NotifyObservers(img_cx->obs_list, message, &message_data);
}
static int
il_init_scaling(il_container *ic)
{
int scaled_width = ic->image->header.width;
/* Allocate temporary scale space */
if (scaled_width != (int)ic->src_header->width)
{
PR_FREEIF(ic->scalerow);
if (!(ic->scalerow = (unsigned char *)PR_MALLOC(scaled_width * 3)))
{
ILTRACE(0,("il: MEM scale row"));
return MK_OUT_OF_MEMORY;
}
}
return 0;
}
/* Allocate and initialize the destination image's transparent_pixel with
the Image Library's preferred transparency color i.e. the background color
passed into IL_GetImage. The image decoder is encouraged to use this
color, but may opt not to do so. */
PRBool
il_init_image_transparent_pixel(il_container *ic)
{
IL_IRGB *img_trans_pixel = ic->image->header.transparent_pixel;
if (!img_trans_pixel) {
img_trans_pixel = PR_NEWZAP(IL_IRGB);
if (!img_trans_pixel)
return PR_FALSE;
if (ic->background_color) {
nsCRT::memcpy(img_trans_pixel, ic->background_color, sizeof(IL_IRGB));
}
else {
/* A mask will always be used if no background color was
requested. */
}
ic->image->header.transparent_pixel = img_trans_pixel;
}
return PR_TRUE;
}
/* Destroy the destination image's transparent pixel. */
void
il_destroy_image_transparent_pixel(il_container *ic)
{
NI_PixmapHeader *img_header = &ic->image->header;
PR_FREEIF(img_header->transparent_pixel);
img_header->transparent_pixel = NULL;
}
/* Inform the Image Library of the source image's dimensions. This function
determines the size of the destination image, and calls the front end to
allocate storage for the destination image's bits. If the source image's
transparent pixel is set, and a background color was not specified for this
image request, then a mask will also be allocated for the destination
image. */
int
il_size(il_container *ic)
{
float aspect;
int status;
PRUint8 img_depth;
PRUint32 src_width, src_height;
int32 image_bytes, old_image_bytes;
IL_GroupContext *img_cx = ic->img_cx;
NI_PixmapHeader *src_header = ic->src_header; /* Source image header. */
NI_PixmapHeader *img_header = &ic->image->header; /* Destination image
header. */
PRUint32 req_w=0, req_h=0; /* store requested values for printing.*/
/* Get the dimensions of the source image. */
src_width = src_header->width;
src_height = src_header->height;
/* Ensure that the source image has a sane area. */
if (!src_width || !src_height ||
(src_width > MAX_IMAGE_WIDTH) ||
(src_height > MAX_IMAGE_HEIGHT)) {
ILTRACE(1,("il: bad image size: %dx%d", src_width, src_height));
return MK_IMAGE_LOSSAGE;
}
ic->state = IC_SIZED;
if (ic->state == IC_MULTI)
return 0;
if(ic->display_type == IL_Printer){
req_w = ic->dest_width;
req_h = ic->dest_height;
ic->dest_width = 0; /*to decode natural size*/
ic->dest_height = 0;
}
/* For now, we don't allow an image to change output size on the
* fly, but we do allow the source image size to change, and thus
* we may need to change the scaling factor to fit it into the
* same size container on the display.
*/
if (ic->sized) {
status = il_init_scaling(ic);
return status;
}
/* This must appear before il_init_img_header. */
old_image_bytes = (int32)img_header->widthBytes * img_header->height;
/* Initialize the dimensions of the destination image header structure
to be the dimensions of the source image. The Display Front End will
reset these dimensions to be the target dimensions of the image if
it does not handle scaling. */
img_header->width = src_width;
img_header->height = src_height;
/* Determine the target dimensions of the image. */
aspect = (float)src_width/(float)src_height;
if (!ic->dest_width && !ic->dest_height) {
/* Both target dimensions were unspecified, so use the dimensions of
the source image. */
ic->dest_width = src_width;
ic->dest_height = src_height;
}
else if (ic->dest_width && ic->dest_height) {
/* Both target dimensions were specified; determine if this causes
aspect ratio distortion. */
if (ic->dest_width * src_height != ic->dest_height * src_width)
ic->aspect_distorted = PR_TRUE;
}
else if (ic->dest_width) {
/* Only the target width was specified. Determine the target height
which preserves aspect ratio. */
ic->dest_height = (int)((float)ic->dest_width / aspect + 0.5);
}
else {
/* Only the target height was specified. Determine the target width
which preserves aspect ratio. */
ic->dest_width = (int)(aspect * (float)ic->dest_height + 0.5);
}
if (ic->dest_width == 0) ic->dest_width = 1;
if (ic->dest_height == 0) ic->dest_height = 1;
/* Determine if the image will be displayed at its natural size. */
if (ic->dest_width == src_width && ic->dest_height == src_height)
ic->natural_size = PR_TRUE;
/* Check that the target image dimensions are reasonable. */
if ((ic->dest_width > MAX_IMAGE_WIDTH) ||
(ic->dest_height > MAX_IMAGE_HEIGHT)) {
return MK_IMAGE_LOSSAGE;
}
ILTRACE(2,("il: size %dx%d, scaled from %dx%d, ic = 0x%08x\n",
ic->dest_width, ic->dest_height, src_width, src_height, ic));
/* Determine the number of bytes per scan-line. Image data must be
quadlet aligned for optimizations. */
img_depth = img_header->color_space->pixmap_depth;
img_header->widthBytes = (img_header->width * img_depth + 7) / 8;
img_header->widthBytes = ROUNDUP(img_header->widthBytes, 4);
/* Create and initialize the mask pixmap structure, if required. A mask
is allocated only if the image is transparent and no background color
was specified for this image request. */
if ((src_header->transparent_pixel && !ic->background_color)
||(img_header->alpha_bits))
{
if (!ic->mask) {
NI_PixmapHeader *mask_header;
if (!(ic->mask = PR_NEWZAP(IL_Pixmap))) {
return MK_OUT_OF_MEMORY;
}
mask_header = &ic->mask->header;
PRBool ret;
if(img_header->alpha_bits)
ret = IL_CreateGreyScaleColorSpace(1, img_header->alpha_bits, &mask_header->color_space);
else
ret = IL_CreateGreyScaleColorSpace(1, 1, &mask_header->color_space);
if ((!ret)||(!mask_header->color_space))
return MK_OUT_OF_MEMORY;
mask_header->alpha_bits = img_header->alpha_bits;
mask_header->is_mask = PR_TRUE;
mask_header->width = img_header->width;
mask_header->height = img_header->height;
if(img_header->alpha_bits == 1)
mask_header->widthBytes = (mask_header->width + 7) / 8;
if(img_header->alpha_bits == 8)
mask_header->widthBytes = mask_header->width;
/* Mask data must be quadlet aligned for optimizations */
mask_header->widthBytes = ROUNDUP(mask_header->widthBytes, 4);
}
/* Notify observers that the image is transparent and has a mask. */
il_transparent_notify(ic);
}
else if (ic->mask) /* for png alpha*/ {
il_transparent_notify(ic);
if(ic->background_color){
/*
src_header->transparent_pixel = ic->background_color;
img_header->transparent_pixel = ic->background_color;
nsCRT::memcpy(img_trans_pixel, ic->background_color, sizeof(IL_IRGB));
*/
}else{
/*
il_destroy_pixmap(ic->img_cb, ic->mask);
ic->mask = NULL;
*/
}
}
ic->sized = 1;
/* Notify observers of the target dimensions of the image. */
ic->imgdcb->ImgDCBHaveHdr(ic->dest_width, ic->dest_height);
/* Fabricate a title for the image and notify observers. Image Plugins
will need to supply information on the image type. */
il_description_notify(ic);
/* If the display front-end doesn't support scaling, IMGCBIF_NewPixmap will
set the image and mask dimensions to scaled_width and scaled_height. */
nsresult rv = img_cx->img_cb->NewPixmap(img_cx->dpy_cx, ic->dest_width,
ic->dest_height, ic->image, ic->mask);
if (NS_FAILED(rv))
return MK_OUT_OF_MEMORY;
if (!ic->image->haveBits)
return MK_OUT_OF_MEMORY;
if (ic->mask && !ic->mask->haveBits)
return MK_OUT_OF_MEMORY;
/* Adjust the total cache byte count to reflect any departure from the
original predicted byte count for this image. */
image_bytes = (int32)img_header->widthBytes * img_header->height;
if (image_bytes - old_image_bytes)
il_adjust_cache_fullness(image_bytes - old_image_bytes);
/* If we have a mask, initialize its bits. */
if (ic->mask) {
NI_PixmapHeader *mask_header = &ic->mask->header;
PRUint32 mask_size = mask_header->widthBytes * mask_header->height;
img_cx->img_cb->ControlPixmapBits(img_cx->dpy_cx, ic->mask,
IL_LOCK_BITS);
memset (ic->mask->bits, ~0, mask_size);
img_cx->img_cb->ControlPixmapBits(img_cx->dpy_cx, ic->mask,
IL_UNLOCK_BITS);
}
if ((status = il_init_scaling(ic)) < 0)
return status;
/* XXXM12N Clean this up */
/* Get memory for quantizing. (required amount depends on image width) */
if (img_header->color_space->type == NI_PseudoColor) {
if (!il_init_quantize(ic)) {
ILTRACE(0,("il: MEM il_init_quantize"));
return MK_OUT_OF_MEMORY;
}
}
if((ic->display_type == IL_Printer)&& (req_w || req_h)){
ic->dest_width = req_w;
ic->dest_height = req_h;
}
return 0;
}
#ifdef XP_OS2
#define IL_SIZE_CHUNK 16384
#else
#define IL_SIZE_CHUNK 128
#endif
#ifdef XP_MAC
# if GENERATINGPOWERPC
# define IL_PREFERRED_CHUNK 8192
# define IL_OFFSCREEN_CHUNK 128
# else /* normal mac */
# define IL_PREFERRED_CHUNK 4096
# define IL_OFFSCREEN_CHUNK 128
# endif
#else /* !XP_MAC */
#ifdef XP_OS2
# define IL_PREFERRED_CHUNK (12*1024)
# define IL_OFFSCREEN_CHUNK ( 6*1024)
#else
# define IL_PREFERRED_CHUNK 2048
# define IL_OFFSCREEN_CHUNK 128
#endif
#endif
int
IL_StreamWriteReady(il_container *ic)
{
nsresult rv= NS_ERROR_FAILURE;
PRUint32 max_read = 0;
if (ic->imgdec)
rv = ic->imgdec->ImgDWriteReady(&max_read);
if(NS_FAILED(rv))
return IL_OFFSCREEN_CHUNK;
if(max_read == 0)
return 0; //send no more data please
/*
* It could be that layout aborted image loading by calling IL_FreeImage
* before the netlib finished transferring data. Don't do anything.
*/
if(ic->state == IC_ABORT_PENDING)
return IL_OFFSCREEN_CHUNK;
if (!ic->sized)
/* A (small) default initial chunk */
return IL_SIZE_CHUNK;
return IL_PREFERRED_CHUNK;
}
/* Given the first few bytes of a stream, identify the image format */
PRBool
sniffout_mimetype(const char *buf, int32 len, char* aContentType)
{
int i;
if (len >= 4 && !nsCRT::strncmp(buf, "GIF8", 4))
{
PR_snprintf(aContentType, 10,"%s", "image/gif");
return PR_TRUE;
}
/* for PNG */
if (len >= 4 && ((unsigned char)buf[0]==0x89 &&
(unsigned char)buf[1]==0x50 &&
(unsigned char)buf[2]==0x4E &&
(unsigned char)buf[3]==0x47))
{
PR_snprintf(aContentType, 10,"%s","image/png");
return PR_TRUE;
}
/* JFIF files start with SOI APP0 but older files can start with SOI DQT
* so we test for SOI followed by any marker, i.e. FF D8 FF
* this will also work for SPIFF JPEG files if they appear in the future.
*
* (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
*/
if (len >= 3 &&
((unsigned char)buf[0])==0xFF &&
((unsigned char)buf[1])==0xD8 &&
((unsigned char)buf[2])==0xFF)
{
PR_snprintf(aContentType, 11,"%s", "image/jpeg");
return PR_TRUE;
}
/* ART begins with JG (4A 47). Major version offset 2.
Minor version offset 3. Offset 4 must be NULL.
*/
if (len >= 5 &&
((unsigned char) buf[0])==0x4a &&
((unsigned char) buf[1])==0x47 &&
((unsigned char) buf[4])==0x00 )
{
PR_snprintf(aContentType, 11,"%s", "image/x-jg");
return PR_TRUE;
}
/* no simple test for XBM vs, say, XPM so punt for now */
if (len >= 8 && !strncmp(buf, "#define ", 8) )
{
/* Don't contradict the given type, since this ID isn't definitive */
PR_snprintf(aContentType, 8,"%s", "unknown");
return PR_TRUE;
}
if (len < 12)
{
ILTRACE(1,("il: too few bytes to determine type"));
PR_snprintf(aContentType, 8,"%s", "unknown");
return PR_FALSE;
}
/* all the servers return different formats so root around */
for (i=0; i<28; i++)
{
if (!strncmp(&buf[i], "Not Fou", 7)){
PR_snprintf(aContentType, 8,"%s", "unknown");
return PR_FALSE;
}
}
//just in case
PR_snprintf(aContentType, 8,"%s", "unknown");
return PR_FALSE;
}
/*
* determine what kind of image data we are dealing with
*/
IL_IMPLEMENT(int)
IL_Type(const char *buf, int32 len)
{
char aContent[200];
return sniffout_mimetype(buf, len, aContent);
}
int
IL_StreamWrite(il_container *ic, const unsigned char *str, int32 len)
{
nsresult rv;
ILTRACE(4, ("il: write with %5d bytes for %s\n", len, ic->url_address));
/*
* Layout may have decided to abort this image in mid-stream,
* but netlib doesn't know about it yet and keeps sending
* us data. Force the netlib to abort.
*/
if (ic->state == IC_ABORT_PENDING)
return -1;
/* Has user hit the stop button ? */
if (il_image_stopped(ic))
return -1;
ic->bytes_consumed += len;
if (len)
rv = ic->imgdec->ImgDWrite(str, len);
/* Notify observers of image progress. */
il_progress_notify(ic);
if(NS_FAILED(rv))
return -1;
else
return len;
}
int
IL_StreamFirstWrite(il_container *ic, const unsigned char *str, int32 len)
{
nsresult rv = NS_OK;
PR_ASSERT(ic);
PR_ASSERT(ic->image);
/* If URL redirection occurs, the url stored in the
image container is the redirect url not the image file url.
If the image is animated, the imglib will never match the
file name in the cache unless you update ic->url_address.
ic->fetch_url keeps the actual url for you.
*/
/* ic->fetch_url is being set in NetReaderImpl::FirstWrite(..).
This is just a backup code to set ic->fetch_url, in the case
we do not set ic->fetch_url in NetReaderImpl::FirstWrite(..).*/
// FREE_IF_NOT_NULL(ic->fetch_url);
if (!ic->fetch_url) {
if (ic->url) {
ic->fetch_url = ic->url->GetAddress();
}
else {
if(ic->url_address) // check needed because of mkicons.c
ic->fetch_url = PL_strdup(ic->url_address);
else
ic->fetch_url = NULL;
}
}
/* Grab the URL's expiration date */
if (ic->url)
ic->expires = ic->url->GetExpires();
/* if our mime sniffer can recognize what's in the
data stream and it is one of our std vanilla types.
Check to see if it matches the mimetype sent by creator.
*/
char contenttype[50];
if(sniffout_mimetype((const char*) str, len, contenttype)){
// data type understood by mime sniffer
if(nsCRT::strcmp(contenttype, ic->type) != 0){
//what the sniff saw and what the mime header said is
//different. We'll believe the data from the sniffer
nsCRT::free(ic->type);
ic->type = nsCRT::strdup(contenttype);
}
}
nsIImgDecoder *imgdec ;
char imgtypestr[200];
PR_snprintf(imgtypestr, sizeof(imgtypestr), "component://netscape/image/decoder&type=%s"
, ic->type );
static NS_DEFINE_IID(kIImgDecoderIID, NS_IIMGDECODER_IID);
rv = nsComponentManager::CreateInstance(imgtypestr, NULL,
kIImgDecoderIID,
(void **)&imgdec);
/* we did our best. Gotta give up. */
if (NS_FAILED(rv)){
return MK_IMAGE_LOSSAGE;
}
imgdec->SetContainer(ic);
// We will already have a decoder instance for this
// image container if the image is animated. For now,
// we release it before taking the new one.
// Later I'll see how animated image containers can
// reuse the decoder for subsequent frames.
if(ic->imgdec)
NS_RELEASE(ic->imgdec);
ic->imgdec = imgdec;
rv = imgdec->ImgDInit();
if(NS_FAILED(rv)){
NS_RELEASE(ic->imgdec);
ic->imgdec = nsnull;
ILTRACE(0,("il: image init failed"));
return MK_OUT_OF_MEMORY;
}
return 0; //ok
}
/* Called when a container is aborted by Netlib. */
static void
il_container_aborted(il_container *ic)
{
IL_ImageReq *image_req;
IL_GroupContext *img_cx;
il_context_list *current;
/* Keep track of the fact that this container was aborted by Netlib,
and guard against multiple calls to this routine. */
if (ic->is_aborted)
return;
ic->is_aborted = PR_TRUE;
/* Notify image observers that the image was aborted. */
for (image_req = ic->clients; image_req; image_req = image_req->next) {
il_icon_notify(image_req, IL_IMAGE_DELAYED, IL_ABORTED);
}
/* Now handle image group observers. */
for (current = ic->img_cx_list; current; current = current->next) {
/* Increment the count of images in this context which were aborted
by Netlib. Observer notification takes place if there were
previously no aborted images in this context. */
img_cx = current->img_cx;
if (!img_cx->num_aborted)
il_group_notify(img_cx, IL_ABORTED_LOADING);
img_cx->num_aborted++;
#ifdef DEBUG_GROUP_OBSERVER
ILTRACE(1,("1 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
img_cx, img_cx->num_containers, img_cx->num_loading,
img_cx->num_looping, img_cx->num_aborted));
#endif /* DEBUG_GROUP_OBSERVER */
}
}
static void
il_bad_container(il_container *ic)
{
IL_ImageReq *image_req;
ILTRACE(4,("il: bad container, sending icon"));
if((ic->type)&&
((nsCRT::strlen(ic->type) < 8) ||
(nsCRT::strncmp(ic->type, "unknown", 7)==0))) {
ic->state = IC_MISSING;
for (image_req = ic->clients; image_req; image_req = image_req->next)
il_icon_notify(image_req, IL_IMAGE_NOT_FOUND, IL_ERROR_NO_DATA);
}
else {
if (ic->state == IC_INCOMPLETE) {
il_container_aborted(ic);
}
else {
for (image_req = ic->clients; image_req;
image_req = image_req->next)
il_icon_notify(image_req, IL_IMAGE_BAD_DATA,
IL_ERROR_IMAGE_DATA_TRUNCATED);
}
}
/* Image container gets deleted later. */
}
/* IL_GetURL completion callback. */
void
IL_NetRequestDone(il_container *ic, ilIURL *url, int status)
{
IL_ImageReq *image_req;
PR_ASSERT(ic);
/* Record the fact that NetLib is done loading. */
if (ic->url == url) {
ic->is_url_loading = PR_FALSE;
}
/* The (ic->url == url) check is for a weird timer issue, see the comment at the
end of this function. */
/*
* It could be that layout aborted image loading by calling IL_DestroyImage
* before the netlib finished transferring data. If so, really do the
* freeing of the data that was deferred there.
*/
if (ic->state == IC_ABORT_PENDING) {
il_delete_container(ic);
NS_RELEASE(url);
return;
}
if (status < 0) {
ILTRACE(2,("il:net done ic=0x%08x, status=%d, ic->state=0x%02x\n",
ic, status, ic->state));
/* Netlib detected failure before a stream was even created. */
if (ic->state < IC_BAD) {
if (status == MK_OBJECT_NOT_IN_CACHE)
ic->state = IC_NOCACHE;
else if (status == MK_UNABLE_TO_LOCATE_FILE)
ic->state = IC_MISSING;
else {
/* status is probably MK_INTERRUPTED */
ic->state = IC_INCOMPLETE; /* try again on reload */
}
if (!ic->sized) {
if (status == MK_OBJECT_NOT_IN_CACHE) {
for (image_req = ic->clients; image_req;
image_req = image_req->next)
il_icon_notify(image_req, IL_IMAGE_DELAYED,
IL_NOT_IN_CACHE);
}
else if (status == MK_INTERRUPTED) {
il_container_aborted(ic);
}
else if (status == MK_UNABLE_TO_LOCATE_FILE) {
for (image_req = ic->clients; image_req;
image_req = image_req->next)
il_icon_notify(image_req, IL_IMAGE_NOT_FOUND,
IL_ERROR_NO_DATA);
}
else {
for (image_req = ic->clients; image_req;
image_req = image_req->next)
il_icon_notify(image_req, IL_IMAGE_BAD_DATA,
IL_ERROR_IMAGE_DATA_ILLEGAL);
}
}
}
}
else {
/* It is possible for NetLib to call the exit routine with a success status,
even though the stream was never created (this can happen when fetching
a mime image part which contains no image data.) Show a missing image
icon. */
if (ic->state < IC_STREAM) {
ic->state = IC_MISSING;
for (image_req = ic->clients; image_req; image_req = image_req->next)
il_icon_notify(image_req, IL_IMAGE_NOT_FOUND, IL_ERROR_NO_DATA);
}
}
if (ic->url == url) {
NS_RELEASE(url);
ic->url = NULL;
}
/* else there is actually another load going on from a looping gif.
* Weird timer issue. If ic->url does not equal url, then il_image_complete was
* already called for "url" and a new load started. ic->url is the url for
* the new load and will be released in its IL_NetRequestDone. "url" was already
* released by special code in il_image_complete.
*/
}
void
il_image_abort(il_container *ic)
{
if (ic->imgdec)
ic->imgdec->ImgDAbort();
/* Clear any pending timeouts */
if (ic->row_output_timeout) {
IL_ClearTimeout(ic->row_output_timeout);
ic->row_output_timeout = NULL;
}
}
void
IL_StreamComplete(il_container *ic, PRBool is_multipart)
{
#ifdef DEBUG
PRTime cur_time;
PRInt32 difference;
#endif /* DEBUG */
PR_ASSERT(ic);
if(ic->type){
nsCRT::free(ic->type);
ic->type = NULL;
}
#ifdef DEBUG
cur_time = PR_Now();
LL_SUB(cur_time, cur_time, ic->start_time);
difference = LL_L2I(difference, cur_time);
ILTRACE(1, ("il: complete: %d seconds for %s\n",
difference, ic->url_address));
#endif /* DEBUG */
ic->is_multipart = is_multipart;
if (ic->imgdec)
ic->imgdec->ImgDComplete();
else
il_image_complete(ic);
}
/* Called when the container has finished loading to update the number of
actively loading image containers in each of the client contexts.
Non-looping images are considered to have been loaded upon natural
completion of network activity for the image. Looping images, on the
other hand, are deemed to have been loaded once the first iteration of
the animation is complete. */
static void
il_container_loaded(il_container *ic)
{
il_context_list *current;
IL_GroupContext *img_cx;
for (current = ic->img_cx_list; current; current = current->next) {
/* Decrement the number of actively loading image containers in the
client context. */
img_cx = current->img_cx;
img_cx->num_loading--;
if (!img_cx->num_loading) {
/* If this is the last image to have loaded in the group,
notify observers that images stopped loading. */
il_group_notify(img_cx, IL_FINISHED_LOADING);
}
#ifdef DEBUG_GROUP_OBSERVER
ILTRACE(1,("2 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
img_cx, img_cx->num_containers, img_cx->num_loading,
img_cx->num_looping, img_cx->num_aborted));
#endif /* DEBUG_GROUP_OBSERVER */
}
}
/* Called when a container starts/stops looping to update the number of
looping image containers in each of the client containers. An animated
image is considered to have started looping at the beginning of the
second iteration. */
static void
il_container_looping(il_container *ic)
{
PRBool is_looping = (PRBool)ic->is_looping;
il_context_list *current;
IL_GroupContext *img_cx;
for (current = ic->img_cx_list; current; current = current->next) {
img_cx = current->img_cx;
if (is_looping) { /* Image has started looping. */
/* Increment the number of looping image containers in the client
context. Observer notification takes place if the image group
previously had no looping images. */
if (!img_cx->num_looping)
il_group_notify(img_cx, IL_STARTED_LOOPING);
img_cx->num_looping++;
#ifdef DEBUG_GROUP_OBSERVER
ILTRACE(1,("3 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
img_cx, img_cx->num_containers, img_cx->num_loading,
img_cx->num_looping, img_cx->num_aborted));
#endif /* DEBUG_GROUP_OBSERVER */
}
else { /* Image has stopped looping. */
/* Decrement the number of looping image containers in the client
context. Observer notification takes place if this was
the last looping image in the group. */
img_cx->num_looping--;
if (!img_cx->num_looping)
il_group_notify(img_cx, IL_FINISHED_LOOPING);
#ifdef DEBUG_GROUP_OBSERVER
ILTRACE(1,("4 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
img_cx, img_cx->num_containers, img_cx->num_loading,
img_cx->num_looping, img_cx->num_aborted));
#endif /* DEBUG_GROUP_OBSERVER */
}
}
}
/* Called when all activity within a container has successfully completed. */
static void
il_container_complete(il_container *ic)
{
IL_GroupContext *img_cx = ic->img_cx;
/* Update the image (and mask) pixmaps for the final time. */
il_flush_image_data(ic);
/* Tell the Front Ends that we will not modify the bits any further. */
img_cx->img_cb->ControlPixmapBits(img_cx->dpy_cx, ic->image,
IL_RELEASE_BITS);
if (ic->mask) {
img_cx->img_cb->ControlPixmapBits(img_cx->dpy_cx, ic->mask,
IL_RELEASE_BITS);
}
if (!ic->is_looping) {
/* Tell the client contexts that the container has finished
loading. We don't do this for looping images which have just
finished looping, since we called il_container_loaded at the
time the first iteration completed. */
il_container_loaded(ic);
}
else {
/* This is a looping image whose loop count has reached zero, so
set the container's state to indicate that it is no longer
looping. */
ic->is_looping = PR_FALSE;
/* Inform the client contexts that the container has stopped
looping. */
il_container_looping(ic);
}
/* Set the container's state to complete. This indicates that the
natural completion of network activity for the image. */
ic->state = IC_COMPLETE;
}
/* XXXM12N This routine needs to be broken up into smaller components. */
void
il_image_complete(il_container *ic)
{
NI_PixmapHeader *src_header = ic->src_header;
IL_DisplayType display_type = ic->display_type;
ilINetReader *reader;
switch( ic->state ){
case IC_ABORT_PENDING:
/* It could be that layout aborted image loading by calling
IL_DestroyImage() before the netlib finished transferring data.
Don't do anything. */
il_scour_container(ic);
break;
case IC_VIRGIN:
case IC_START:
case IC_STREAM:
/* If we didn't size the image, but the stream finished loading, the
image must be corrupt or truncated. */
ic->state = IC_BAD;
il_bad_container(ic);
break;
case IC_SIZED:
case IC_MULTI:
PR_ASSERT(ic->image && ic->image->haveBits);
ILTRACE(1,("il: complete %d image width %d (%d) height %d,"
" depth %d, %d colors",
ic->multi,
ic->image->header.width,
ic->image->header.widthBytes,
ic->image->header.height,
ic->image->header.color_space->pixmap_depth,
ic->image->header.color_space->cmap.num_colors));
/* 3 cases: simple, multipart MIME, multi-image animation */
if (!ic->loop_count && !ic->is_multipart) {
/* A single frame, single part image. */
il_container_complete(ic);
}
else {
/* Display the rest of the last image before starting a new one */
il_flush_image_data(ic);
/* Force new colormap to be loaded in case its different from the
* LOSRC or previous images in the multipart message.
* XXX - fur - Shouldn't do this for COLORMAP case.
*/
/* PRBool ret= */ il_reset_palette(ic);
FREE_IF_NOT_NULL(src_header->color_space->cmap.map);
FREE_IF_NOT_NULL(src_header->transparent_pixel);
il_destroy_image_transparent_pixel(ic);
FREE_IF_NOT_NULL(ic->comment);
ic->comment_length = 0;
/* Handle looping, which can be used to replay an animation. */
if (ic->loop_count) {
int is_infinite_loop = (ic->loop_count == -1);
ilIURL *netRequest = NULL;
if (!is_infinite_loop)
ic->loop_count--;
ILTRACE(1,("il: loop %s", ic->url_address));
if(ic->net_cx){ //ptn test
netRequest = ic->net_cx->CreateURL(ic->fetch_url, IMG_NTWK_SERVER);
if (!netRequest) { /* OOM */
il_container_complete(ic);
break;
}
}else{
il_container_complete(ic);
break;
}
/* Only loop if the image stream is available locally.
Also, if the user hit the "stop" button, don't
allow the animation to loop. */
#ifdef NU_CACHE
if ((ic->net_cx->IsLocalFileURL(ic->fetch_url) ||
ic->net_cx->IsURLInCache(netRequest)) &&
(!il_image_stopped(ic)) &&
ic->net_cx &&
(display_type == IL_Console))
#else
if ((ic->net_cx->IsLocalFileURL(ic->fetch_url) ||
ic->net_cx->IsURLInMemCache(netRequest) ||
ic->net_cx->IsURLInDiskCache(netRequest)) &&
(!il_image_stopped(ic)) &&
ic->net_cx &&
(display_type == IL_Console))
#endif
{
if (!ic->is_looping) {
/* If this is the end of the first pass of the
animation, then set the state of the container
to indicate that we have started looping. */
ic->is_looping = PR_TRUE;
/* At this point the animation is considered to have
loaded, so we need to tell the client contexts that
the container has loaded. */
il_container_loaded(ic);
/* This is also the point at which the animation is
considered to have started looping, so inform the
client contexts accordingly. */
il_container_looping(ic);
}
ic->bytes_consumed = 0;
ic->state = IC_START;
/* This is to deal with a weird timer bug, see the comment at the
end of IL_NetRequestDone. */
NS_IF_RELEASE(ic->url);
ic->url = netRequest;
/* Record the fact that we are calling NetLib to load a URL. */
ic->is_url_loading = PR_TRUE;
/* Suppress thermo & progress bar */
/* Call to netlib for net cache data happens here. */
netRequest->SetBackgroundLoad(PR_TRUE);
reader = IL_NewNetReader(ic);
(void) ic->net_cx->GetURL(ic->url, IMG_NTWK_SERVER, reader);
/* Release reader, GetURL will keep a ref to it. */
NS_RELEASE(reader);
} else {
ic->loop_count = 0;
NS_RELEASE(netRequest);
il_container_complete(ic);
}
}
else if (ic->is_multipart) {
ic->multi++;
ic->state = IC_MULTI;
}
}
break;
case IC_MISSING:
case IC_NOCACHE:
default:
break;
}/*switch*/
/* Clear any pending timeouts */
if (ic->row_output_timeout) {
IL_ClearTimeout(ic->row_output_timeout);
ic->row_output_timeout = NULL;
}
/* Notify observers that we are done decoding. */
if (ic->state != IC_ABORT_PENDING && ic->state != IC_BAD)
il_image_complete_notify(ic);
}
void
IL_StreamAbort(il_container *ic, int status)
{
int old_state;
IL_ImageReq *image_req;
PR_ASSERT(ic);
ILTRACE(4,("il: abort, status=%d ic->state=%d", status, ic->state));
/* Abort the image. */
il_image_abort(ic);
if(ic->type){
nsCRT::free(ic->type);
ic->type = NULL;
}
if(ic->state >= IC_SIZED || (ic->state == IC_ABORT_PENDING)){
if (status == MK_INTERRUPTED){
il_container_aborted(ic);
}
else{
for (image_req = ic->clients; image_req; image_req = image_req->next)
il_icon_notify(image_req, IL_IMAGE_BAD_DATA,
IL_ERROR_IMAGE_DATA_ILLEGAL);
}
}
/*
* It could be that layout aborted image loading by calling IL_DestroyImage()
* before the netlib finished transferring data. Don't do anything.
*/
if (ic->state == IC_ABORT_PENDING)
return;
old_state = ic->state;
if (status == MK_INTERRUPTED)
ic->state = IC_INCOMPLETE;
else
ic->state = IC_BAD;
if (old_state < IC_SIZED)
il_bad_container(ic);
}
PRBool
IL_StreamCreated(il_container *ic,
ilIURL *url,
char* type)
{
PR_ASSERT(ic);
/*
* It could be that layout aborted image loading by calling IL_FreeImage()
* before the netlib finished transferring data. Don't do anything.
*/
if (ic->state == IC_ABORT_PENDING)
return PR_FALSE;
if(ic->type != NULL)
nsCRT::free(ic->type); //can be !NULL in animated gifs.
ic->type = nsCRT::strdup(type); //mime string
ic->content_length = url->GetContentLength();
char* addr = url->GetAddress();
ILTRACE(4,("il: new stream, type %s, %s", ic->type, addr));
nsCRT::free(addr);
ic->state = IC_STREAM;
#ifndef M12N /* XXXM12N Fix me. */
#ifdef XP_MAC
ic->image->hasUniqueColormap = PR_FALSE;
#endif
#endif /* M12N */
return PR_TRUE;
}
/* Phong's linear congruential hash */
PRUint32
il_hash(const char *ubuf)
{
unsigned char * buf = (unsigned char*) ubuf;
PRUint32 h=1;
while(*buf)
{
h = 0x63c63cd9*h + 0x9c39c33d + (int32)*buf;
buf++;
}
return h;
}
#define IL_LAST_ICON 62
/* Extra factor of 7 is to account for duplications between
mc-icons and ns-icons */
static PRUint32 il_icon_table[(IL_LAST_ICON + 7) * 2];
static void
il_setup_icon_table(void)
{
int inum = 0;
/* gopher/ftp icons */
il_icon_table[inum++] = il_hash("internal-gopher-text");
il_icon_table[inum++] = IL_GOPHER_TEXT;
il_icon_table[inum++] = il_hash("internal-gopher-image");
il_icon_table[inum++] = IL_GOPHER_IMAGE;
il_icon_table[inum++] = il_hash("internal-gopher-binary");
il_icon_table[inum++] = IL_GOPHER_BINARY;
il_icon_table[inum++] = il_hash("internal-gopher-sound");
il_icon_table[inum++] = IL_GOPHER_SOUND;
il_icon_table[inum++] = il_hash("internal-gopher-movie");
il_icon_table[inum++] = IL_GOPHER_MOVIE;
il_icon_table[inum++] = il_hash("internal-gopher-menu");
il_icon_table[inum++] = IL_GOPHER_FOLDER;
il_icon_table[inum++] = il_hash("internal-gopher-index");
il_icon_table[inum++] = IL_GOPHER_SEARCHABLE;
il_icon_table[inum++] = il_hash("internal-gopher-telnet");
il_icon_table[inum++] = IL_GOPHER_TELNET;
il_icon_table[inum++] = il_hash("internal-gopher-unknown");
il_icon_table[inum++] = IL_GOPHER_UNKNOWN;
/* news icons */
il_icon_table[inum++] = il_hash("internal-news-catchup-group");
il_icon_table[inum++] = IL_NEWS_CATCHUP;
il_icon_table[inum++] = il_hash("internal-news-catchup-thread");
il_icon_table[inum++] = IL_NEWS_CATCHUP_THREAD;
il_icon_table[inum++] = il_hash("internal-news-followup");
il_icon_table[inum++] = IL_NEWS_FOLLOWUP;
il_icon_table[inum++] = il_hash("internal-news-go-to-newsrc");
il_icon_table[inum++] = IL_NEWS_GOTO_NEWSRC;
il_icon_table[inum++] = il_hash("internal-news-next-article");
il_icon_table[inum++] = IL_NEWS_NEXT_ART;
il_icon_table[inum++] = il_hash("internal-news-next-article-gray");
il_icon_table[inum++] = IL_NEWS_NEXT_ART_GREY;
il_icon_table[inum++] = il_hash("internal-news-next-thread");
il_icon_table[inum++] = IL_NEWS_NEXT_THREAD;
il_icon_table[inum++] = il_hash("internal-news-next-thread-gray");
il_icon_table[inum++] = IL_NEWS_NEXT_THREAD_GREY;
il_icon_table[inum++] = il_hash("internal-news-post");
il_icon_table[inum++] = IL_NEWS_POST;
il_icon_table[inum++] = il_hash("internal-news-prev-article");
il_icon_table[inum++] = IL_NEWS_PREV_ART;
il_icon_table[inum++] = il_hash("internal-news-prev-article-gray");
il_icon_table[inum++] = IL_NEWS_PREV_ART_GREY;
il_icon_table[inum++] = il_hash("internal-news-prev-thread");
il_icon_table[inum++] = IL_NEWS_PREV_THREAD;
il_icon_table[inum++] = il_hash("internal-news-prev-thread-gray");
il_icon_table[inum++] = IL_NEWS_PREV_THREAD_GREY;
il_icon_table[inum++] = il_hash("internal-news-reply");
il_icon_table[inum++] = IL_NEWS_REPLY;
il_icon_table[inum++] = il_hash("internal-news-rtn-to-group");
il_icon_table[inum++] = IL_NEWS_RTN_TO_GROUP;
il_icon_table[inum++] = il_hash("internal-news-show-all-articles");
il_icon_table[inum++] = IL_NEWS_SHOW_ALL_ARTICLES;
il_icon_table[inum++] = il_hash("internal-news-show-unread-articles");
il_icon_table[inum++] = IL_NEWS_SHOW_UNREAD_ARTICLES;
il_icon_table[inum++] = il_hash("internal-news-subscribe");
il_icon_table[inum++] = IL_NEWS_SUBSCRIBE;
il_icon_table[inum++] = il_hash("internal-news-unsubscribe");
il_icon_table[inum++] = IL_NEWS_UNSUBSCRIBE;
il_icon_table[inum++] = il_hash("internal-news-newsgroup");
il_icon_table[inum++] = IL_NEWS_FILE;
il_icon_table[inum++] = il_hash("internal-news-newsgroups");
il_icon_table[inum++] = IL_NEWS_FOLDER;
/* httpd file icons */
il_icon_table[inum++] = il_hash("/mc-icons/menu.gif");
il_icon_table[inum++] = IL_GOPHER_FOLDER;
il_icon_table[inum++] = il_hash("/mc-icons/unknown.gif");
il_icon_table[inum++] = IL_GOPHER_UNKNOWN;
il_icon_table[inum++] = il_hash("/mc-icons/text.gif");
il_icon_table[inum++] = IL_GOPHER_TEXT;
il_icon_table[inum++] = il_hash("/mc-icons/image.gif");
il_icon_table[inum++] = IL_GOPHER_IMAGE;
il_icon_table[inum++] = il_hash("/mc-icons/sound.gif");
il_icon_table[inum++] = IL_GOPHER_SOUND;
il_icon_table[inum++] = il_hash("/mc-icons/movie.gif");
il_icon_table[inum++] = IL_GOPHER_MOVIE;
il_icon_table[inum++] = il_hash("/mc-icons/binary.gif");
il_icon_table[inum++] = IL_GOPHER_BINARY;
/* Duplicate httpd icons, but using new naming scheme. */
il_icon_table[inum++] = il_hash("/ns-icons/menu.gif");
il_icon_table[inum++] = IL_GOPHER_FOLDER;
il_icon_table[inum++] = il_hash("/ns-icons/unknown.gif");
il_icon_table[inum++] = IL_GOPHER_UNKNOWN;
il_icon_table[inum++] = il_hash("/ns-icons/text.gif");
il_icon_table[inum++] = IL_GOPHER_TEXT;
il_icon_table[inum++] = il_hash("/ns-icons/image.gif");
il_icon_table[inum++] = IL_GOPHER_IMAGE;
il_icon_table[inum++] = il_hash("/ns-icons/sound.gif");
il_icon_table[inum++] = IL_GOPHER_SOUND;
il_icon_table[inum++] = il_hash("/ns-icons/movie.gif");
il_icon_table[inum++] = IL_GOPHER_MOVIE;
il_icon_table[inum++] = il_hash("/ns-icons/binary.gif");
il_icon_table[inum++] = IL_GOPHER_BINARY;
/* ... and names for all the image icons */
il_icon_table[inum++] = il_hash("internal-icon-delayed");
il_icon_table[inum++] = IL_IMAGE_DELAYED;
il_icon_table[inum++] = il_hash("internal-icon-notfound");
il_icon_table[inum++] = IL_IMAGE_NOT_FOUND;
il_icon_table[inum++] = il_hash("internal-icon-baddata");
il_icon_table[inum++] = IL_IMAGE_BAD_DATA;
il_icon_table[inum++] = il_hash("internal-icon-insecure");
il_icon_table[inum++] = IL_IMAGE_INSECURE;
il_icon_table[inum++] = il_hash("internal-icon-embed");
il_icon_table[inum++] = IL_IMAGE_EMBED;
/* This belongs up in the `news icons' section */
il_icon_table[inum++] = il_hash("internal-news-followup-and-reply");
il_icon_table[inum++] = IL_NEWS_FOLLOWUP_AND_REPLY;
/* editor icons. */
il_icon_table[inum++] = il_hash("internal-edit-named-anchor");
il_icon_table[inum++] = IL_EDIT_NAMED_ANCHOR;
il_icon_table[inum++] = il_hash("internal-edit-form-element");
il_icon_table[inum++] = IL_EDIT_FORM_ELEMENT;
il_icon_table[inum++] = il_hash("internal-edit-unsupported-tag");
il_icon_table[inum++] = IL_EDIT_UNSUPPORTED_TAG;
il_icon_table[inum++] = il_hash("internal-edit-unsupported-end-tag");
il_icon_table[inum++] = IL_EDIT_UNSUPPORTED_END_TAG;
il_icon_table[inum++] = il_hash("internal-edit-java");
il_icon_table[inum++] = IL_EDIT_JAVA;
il_icon_table[inum++] = il_hash("internal-edit-PLUGIN");
il_icon_table[inum++] = IL_EDIT_PLUGIN;
/* Security Advisor and S/MIME icons */
il_icon_table[inum++] = il_hash("internal-sa-signed");
il_icon_table[inum++] = IL_SA_SIGNED;
il_icon_table[inum++] = il_hash("internal-sa-encrypted");
il_icon_table[inum++] = IL_SA_ENCRYPTED;
il_icon_table[inum++] = il_hash("internal-sa-nonencrypted");
il_icon_table[inum++] = IL_SA_NONENCRYPTED;
il_icon_table[inum++] = il_hash("internal-sa-signed-bad");
il_icon_table[inum++] = IL_SA_SIGNED_BAD;
il_icon_table[inum++] = il_hash("internal-sa-encrypted-bad");
il_icon_table[inum++] = IL_SA_ENCRYPTED_BAD;
il_icon_table[inum++] = il_hash("internal-smime-attached");
il_icon_table[inum++] = IL_SMIME_ATTACHED;
il_icon_table[inum++] = il_hash("internal-smime-signed");
il_icon_table[inum++] = IL_SMIME_SIGNED;
il_icon_table[inum++] = il_hash("internal-smime-encrypted");
il_icon_table[inum++] = IL_SMIME_ENCRYPTED;
il_icon_table[inum++] = il_hash("internal-smime-encrypted-signed");
il_icon_table[inum++] = IL_SMIME_ENC_SIGNED;
il_icon_table[inum++] = il_hash("internal-smime-signed-bad");
il_icon_table[inum++] = IL_SMIME_SIGNED_BAD;
il_icon_table[inum++] = il_hash("internal-smime-encrypted-bad");
il_icon_table[inum++] = IL_SMIME_ENCRYPTED_BAD;
il_icon_table[inum++] = il_hash("internal-smime-encrypted-signed-bad");
il_icon_table[inum++] = IL_SMIME_ENC_SIGNED_BAD;
/* LibMsg Attachment Icon */
il_icon_table[inum++] = il_hash("internal-attachment-icon");
il_icon_table[inum++] = IL_MSG_ATTACH;
PR_ASSERT(inum <= (int)(sizeof(il_icon_table) / sizeof(il_icon_table[0])));
}
static PRUint32
il_internal_image(const char *image_url)
{
int i;
PRUint32 hash = il_hash(image_url);
if (il_icon_table[0]==0)
il_setup_icon_table();
for (i=0; i < (int)(sizeof(il_icon_table) / sizeof(il_icon_table[0])); i++)
{
if (il_icon_table[i<<1] == hash)
{
return il_icon_table[(i<<1)+1];
}
}
return 0;
}
/* block certain hosts from loading images */
PRBool il_PermitLoad(const char * image_url, nsIImageRequestObserver * aObserver) {
/* convert image_url to an nsIURL so we can extract host and scheme */
nsresult rv;
NS_WITH_SERVICE(nsIURL, uri, "component://netscape/network/standard-url", &rv);
if (NS_FAILED(rv) || NS_FAILED(uri->SetSpec(image_url))) {
return PR_TRUE;
}
/* extract scheme -- we block loading of only http and https images */
char * scheme;
rv = uri->GetScheme(&scheme);
if (NS_FAILED(rv)) {
return PR_TRUE;
}
if (PL_strcasecmp(scheme, "http") && PL_strcasecmp(scheme, "https")) {
Recycle(scheme);
return PR_TRUE;
}
Recycle(scheme);
/* extract host */
char * host;
rv = uri->GetHost(&host);
if (NS_FAILED(rv)) {
return PR_TRUE;
}
/* obtain first url from aObserver */
if (!aObserver) {
Recycle(host);
return PR_TRUE;
}
#ifdef xxx
/* real ugly hack until I figure out the right way to get the presContext from aObserver */
nsIPresContext* presContext = (nsIPresContext*)(((PRInt32 *)aObserver)[3]);
nsIURI * firstUri;
rv = presContext->GetBaseURL(&firstUri);
if (NS_FAILED(rv) || !firstUri) {
Recycle(host);
return PR_TRUE;
}
char* firstHost = nsnull;
rv = firstUri->GetHost(&firstHost);
NS_RELEASE(firstUri);
if (NS_FAILED(rv)) {
Recycle(host);
return PR_TRUE;
}
#else
char* firstHost = PL_strdup(host);
#endif
/* check to see if we need to block image from loading */
NS_WITH_SERVICE(nsICookieService, cookieservice, kCookieServiceCID, &rv);
if (NS_FAILED(rv)) {
Recycle(host);
Recycle(firstHost);
return PR_TRUE;
}
PRBool permission;
rv = cookieservice->Image_CheckForPermission(host, firstHost, permission);
Recycle(host);
Recycle(firstHost);
if (NS_FAILED(rv)) {
return PR_TRUE;
}
return permission;
}
IL_IMPLEMENT(IL_ImageReq *)
IL_GetImage(const char* image_url,
IL_GroupContext *img_cx,
XP_ObserverList obs_list,
NI_IRGB *background_color,
PRUint32 req_width, PRUint32 req_height,
PRUint32 flags,
void *opaque_cx,
nsIImageRequestObserver * aObserver)
{
/* block certain hosts from loading images */
if (!il_PermitLoad(image_url, aObserver)) {
return NULL;
}
ilINetContext *net_cx = (ilINetContext *)opaque_cx;
NET_ReloadMethod cache_reload_policy = net_cx->GetReloadPolicy();
ilIURL *url = NULL;
IL_ImageReq *image_req;
ilINetReader *reader;
il_container *ic = NULL;
int req_depth = img_cx->color_space->pixmap_depth;
int err;
int is_view_image;
/* Create a new instance for this image request. */
image_req = PR_NEWZAP(IL_ImageReq);
if (!image_req)
return NULL;
image_req->img_cx = img_cx;
/*
* Remember the net context for this image request, It is possible that
* the lifetime of the image container's net context will not be as
* long as that of the image request itself (for example if the image
* request for which the container was originally created happens to
* be destroyed,) in which case we may need to give the container a
* handle on this backup net context.
*/
image_req->net_cx = net_cx->Clone();
if (!image_req->net_cx) {
PR_FREEIF(image_req);
return NULL;
}
image_req->obs_list = obs_list;
XP_SetObserverListObservable(obs_list, (void *)image_req);
ILTRACE(1, ("il: IL_GetImage, url=%s\n", image_url));
if (!image_url)
{
ILTRACE(0,("il: no url, sending delayed icon"));
il_icon_notify(image_req, IL_IMAGE_DELAYED, IL_ERROR_NO_DATA);
return image_req;
}
/* Check for any special internal-use URLs */
if (*image_url == 'i' ||
!PL_strncmp(image_url, "/mc-", 4) ||
!PL_strncmp(image_url, "/ns-", 4))
{
PRUint32 icon;
/* A built-in icon ? */
icon = il_internal_image(image_url);
if (icon)
{
ILTRACE(4,("il: internal icon %d", icon));
/* XXXM12N In response to this notification, layout should set
lo_image->image_attr->attrmask |= LO_ATTR_INTERNAL_IMAGE; */
il_icon_notify(image_req, icon, IL_INTERNAL_IMAGE);
return image_req;
}
}
ic = il_get_container(img_cx, cache_reload_policy, image_url,
background_color, img_cx->dither_mode, req_depth,
req_width, req_height);
if (!ic)
{
ILTRACE(0,("il: MEM il_container"));
il_icon_notify(image_req, IL_IMAGE_DELAYED, IL_ERROR_INTERNAL);
return image_req;
}
/* Give the client a handle into the imagelib world. */
image_req->ic = ic;
is_view_image = PR_FALSE;
if (!il_add_client(img_cx, ic, image_req, is_view_image))
{
il_icon_notify(image_req, IL_IMAGE_DELAYED, IL_ERROR_INTERNAL);
return image_req;
}
/* If the image is already in memory ... */
if (ic->state != IC_VIRGIN) {
switch (ic->state) {
case IC_BAD:
il_icon_notify(image_req, IL_IMAGE_BAD_DATA,
IL_ERROR_IMAGE_DATA_ILLEGAL);
break;
case IC_MISSING:
il_icon_notify(image_req, IL_IMAGE_NOT_FOUND, IL_ERROR_NO_DATA);
break;
case IC_INCOMPLETE:
il_icon_notify(image_req, IL_IMAGE_DELAYED,
IL_ERROR_IMAGE_DATA_TRUNCATED);
break;
case IC_SIZED:
case IC_MULTI:
/* This is a cached image that hasn't completed decoding. */
il_cache_return_notify(image_req);
break;
case IC_COMPLETE:
#ifndef M12N
/* M12N Fix custom colormaps. */
il_set_color_palette(cx, ic);
#else
#endif /* M12N */
/* This is a cached image that has already completed decoding. */
il_cache_return_notify(image_req);
break;
case IC_START:
case IC_STREAM:
case IC_ABORT_PENDING:
case IC_NOCACHE:
break;
default:
PR_ASSERT(0);
//NS_RELEASE(image_req->net_cx);
//PR_FREEIF(image_req);
/* This takes the image_req out of the ic client list,
frees image_req->new_cx, frees image_req.*/
il_delete_client(ic, image_req);
return NULL;
}
/* NOCACHE falls through to be tried again */
if (ic->state != IC_NOCACHE)
return image_req;
}
/* This is a virgin (never-used) image container. */
else
{
ic->forced = FORCE_RELOAD(cache_reload_policy);
}
ic->state = IC_START;
#ifdef DEBUG
ic->start_time = PR_Now();
#endif
/* Record the context that actually initiates and controls the transfer. */
ic->net_cx = net_cx->Clone();
/* need to make a net request */
ILTRACE(1,("il: net request for %s, %s", image_url,
ic->forced ? "force" : ""));
url = ic->net_cx->CreateURL(image_url, cache_reload_policy);
if (!url)
{
// NS_IF_RELEASE(image_req->net_cx);
// PR_FREEIF(image_req);
/* This takes the image_req out of the ic client list,
frees image_req->new_cx, frees image_req.
*/
il_delete_client(ic, image_req);
return NULL;
}
#if 0
/* Add the referer to the URL. */
ic->net_cx->AddReferer(url);
#endif
ic->is_looping = PR_FALSE;
ic->url = url;
/* Record the fact that we are calling NetLib to load a URL. */
ic->is_url_loading = PR_TRUE;
/* save away the container */
reader = IL_NewNetReader(ic);
if (!reader) {
//NS_RELEASE(image_req->net_cx);
//PR_FREEIF(image_req);
/* This takes the image_req out of the ic client list,
frees image_req->new_cx, frees image_req.
*/
il_delete_client(ic, image_req);
return NULL;
}
err = ic->net_cx->GetURL(url, cache_reload_policy, reader);
/* Release reader, GetURL will keep a ref to it. */
NS_RELEASE(reader);
return image_req;
}
IL_IMPLEMENT(void)
IL_ReloadImages(IL_GroupContext *img_cx, void *net_cx)
{
/* XXXM12N - Implement me. */
}
IL_IMPLEMENT(void)
IL_SetDisplayMode(IL_GroupContext *img_cx, PRUint32 display_flags,
IL_DisplayData *display_data)
{
if (display_flags & IL_DISPLAY_CONTEXT)
img_cx->dpy_cx = display_data->display_context;
if (display_flags & IL_DISPLAY_TYPE)
img_cx->display_type = display_data->display_type;
if (display_flags & IL_COLOR_SPACE) {
if (img_cx->color_space)
IL_ReleaseColorSpace(img_cx->color_space);
img_cx->color_space = display_data->color_space;
IL_AddRefToColorSpace(img_cx->color_space);
}
if (display_flags & IL_PROGRESSIVE_DISPLAY)
img_cx->progressive_display = display_data->progressive_display;
if (display_flags & IL_DITHER_MODE)
if (img_cx->color_space && img_cx->color_space->pixmap_depth == 1) {
/* Dithering always on in monochrome mode. */
img_cx->dither_mode = IL_Dither;
}
else {
img_cx->dither_mode = display_data->dither_mode;
}
}
/* Interrupts all images loading in a context. Specifically, stops all
looping image animations. */
IL_IMPLEMENT(void)
IL_InterruptContext(IL_GroupContext *img_cx)
{
il_container *ic;
IL_ImageReq *image_req;
il_container_list *ic_list;
if (!img_cx)
return;
/* Mark all clients in this context as interrupted. */
for (ic_list = img_cx->container_list; ic_list; ic_list = ic_list->next) {
ic = ic_list->ic;
for (image_req = ic->clients; image_req; image_req = image_req->next) {
if (image_req->img_cx == img_cx) {
image_req->stopped = PR_TRUE;
}
}
}
}
/* Interrupts just a specific image request */
IL_IMPLEMENT(void)
IL_InterruptRequest(IL_ImageReq *image_req)
{
if (image_req != NULL) {
image_req->stopped = PR_TRUE;
}
}
/* Has the user aborted the image load ? */
PRBool
il_image_stopped(il_container *ic)
{
IL_ImageReq *image_req;
for (image_req = ic->clients; image_req; image_req = image_req->next) {
if (!image_req->stopped)
return PR_FALSE;
}
/* All clients must be stopped for image container to become dormant. */
return PR_TRUE;
}
/* Create an IL_GroupContext, which represents an aggregation of state
for one or more images and which contains an interface to access
external service functions and callbacks. IL_NewGroupContext will use
the IMGCBIF_AddRef callback to increment the reference count for the
interface.
The dpy_cx argument is opaque to the image library and is passed back to
all of the callbacks in the IMGCBIF interface. */
IL_IMPLEMENT(IL_GroupContext*)
IL_NewGroupContext(void *dpy_cx,
ilIImageRenderer *img_cb)
{
IL_GroupContext *img_cx;
if (!img_cb)
return NULL;
img_cx = (IL_GroupContext*)PR_NEWZAP(IL_GroupContext);
if (!img_cx)
return NULL;
img_cx->dpy_cx = dpy_cx;
img_cx->img_cb = img_cb;
img_cx->progressive_display = PR_TRUE;
/* Create an observer list for the image context. */
if (XP_NewObserverList((void *)img_cx, &img_cx->obs_list)
== MK_OUT_OF_MEMORY) {
PR_FREEIF(img_cx);
return NULL;
}
/* Add the context to the global image context list. */
img_cx->next = il_global_img_cx_list;
il_global_img_cx_list = img_cx;
return img_cx;
}
/* Free an image context. IL_DestroyGroupContext will make a call
to the IMGCBIF_Release callback function of the JMC interface prior to
releasing the IL_GroupContext structure. The IMGCBIF_Release callback
is expected to decrement the reference count for the IMGCBIF interface,
and to free the callback vtable and the interface structure if the
reference count is zero. */
IL_IMPLEMENT(void)
IL_DestroyGroupContext(IL_GroupContext *img_cx)
{
if (img_cx) {
/* Remove ourself from the global image context list. */
if (img_cx == il_global_img_cx_list) {
il_global_img_cx_list = NULL;
}
else {
IL_GroupContext *current, *next;
for (current = il_global_img_cx_list; current; current = next) {
next = current->next;
if (next == img_cx) {
current->next = next->next;
break;
}
}
}
/* Destroy any images remaining in the context. */
if (img_cx->num_containers) {
PR_ASSERT(img_cx->container_list != NULL);
IL_DestroyImageGroup(img_cx);
}
/* Destroy the observer list. */
XP_DisposeObserverList(img_cx->obs_list);
/* Release the image context's reference to the colorspace. */
if (img_cx->color_space) {
IL_ReleaseColorSpace(img_cx->color_space);
img_cx->color_space = NULL;
}
/* Release the img context callback interface. */
NS_RELEASE(img_cx->img_cb);
PR_FREEIF(img_cx);
}
}
/* Add an observer/closure pair to an image group context's observer list.
Returns PR_TRUE if the observer is successfully registered. */
IL_IMPLEMENT(PRBool)
IL_AddGroupObserver(IL_GroupContext *img_cx, XP_ObserverProc observer,
void *closure)
{
return (PRBool)(!XP_AddObserver(img_cx->obs_list, observer, closure));
}
/* Remove an observer/closure pair from an image group context's observer
list. Returns PR_TRUE if successful. */
IL_IMPLEMENT(PRBool)
IL_RemoveGroupObserver(IL_GroupContext *img_cx, XP_ObserverProc observer,
void *closure)
{
return XP_RemoveObserver(img_cx->obs_list, observer, closure);
}
/* Return the dimensions of an icon. */
IL_IMPLEMENT(void)
IL_GetIconDimensions(IL_GroupContext *img_cx, int icon_number, int *width,
int *height)
{
/* Check for a NULL image context. */
if (!img_cx)
return;
/* Obtain the dimensions of the icon. */
img_cx->img_cb->GetIconDimensions(img_cx->dpy_cx, width, height,
icon_number);
}
/* Return the image IL_Pixmap associated with an image request. */
IL_IMPLEMENT(IL_Pixmap *)
IL_GetImagePixmap(IL_ImageReq *image_req)
{
if ((image_req && image_req->ic)&&
(image_req->ic->state == IC_COMPLETE)||(image_req->ic->state == IC_SIZED))
return image_req->ic->image;
else
return NULL;
}
/* Return the mask IL_Pixmap associated with an image request. */
IL_IMPLEMENT(IL_Pixmap *)
IL_GetMaskPixmap(IL_ImageReq *image_req)
{
if (image_req && image_req->ic)
return image_req->ic->mask;
else
return NULL;
}
/* Return the natural dimensions of the image. Returns 0,0 if the dimensions
are unknown. */
IL_IMPLEMENT(void)
IL_GetNaturalDimensions(IL_ImageReq *image_req, int *width, int *height)
{
NI_PixmapHeader *src_header;
if (width)
*width = 0;
if (height)
*height = 0;
if (!image_req || !image_req->ic)
return;
src_header = image_req->ic->src_header;
if (src_header) {
if (width)
*width = src_header->width;
if (height)
*height = src_header->height;
}
}