зеркало из https://github.com/mozilla/gecko-dev.git
2263 строки
55 KiB
C++
2263 строки
55 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||
*
|
||
* The contents of this file are subject to the Netscape Public License
|
||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||
* http://www.mozilla.org/NPL/
|
||
*
|
||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||
* for the specific language governing rights and limitations under the
|
||
* NPL.
|
||
*
|
||
* The Initial Developer of this code under the NPL is Netscape
|
||
* Communications Corporation. Portions created by Netscape are
|
||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||
* Reserved.
|
||
*/
|
||
|
||
#define JMC_INIT_IMGCB_ID
|
||
|
||
// Netscape
|
||
// #include "mdmacmem.h"
|
||
#include "client.h"
|
||
#include "xp_mcom.h"
|
||
#include "merrors.h"
|
||
#include "xpassert.h"
|
||
#include "mimages.h"
|
||
#include "miconutils.h"
|
||
#include "il_util.h"
|
||
#include "il_icons.h"
|
||
#include "CBrowserContext.h"
|
||
#include "CBrowserWindow.h" // for CBrowserWindow::ClipOutPopdown()
|
||
#include "CIconContext.h"
|
||
#include "CHTMLView.h"
|
||
#include "RandomFrontEndCrap.h"
|
||
#include <UDrawingState.h>
|
||
#include "UTextBox.h"
|
||
#include "uerrmgr.h"
|
||
#include "resgui.h"
|
||
#include "CIconCache.h"
|
||
#include "MacMemAllocator.h"
|
||
#include "xlate.h"
|
||
|
||
#ifdef PROFILE
|
||
#pragma profile on
|
||
#endif
|
||
|
||
|
||
#define TRACK_IMAGE_CACHE_SIZE 0
|
||
|
||
#pragma mark --- TYPES ---
|
||
|
||
/*
|
||
* We store color spaces for the following bit depths:
|
||
* 1, 2, 4, 8, 2gray, 4gray, 8gray, 16, 32.
|
||
* Grayscale spaces are lower than colour spaces so that we can do a numerical compare
|
||
* on indices.
|
||
*/
|
||
|
||
enum ColorSpaceIndex {
|
||
kFirstColorSpace = 0,
|
||
kOneBit = 0,
|
||
kEightBitGray,
|
||
kEightBitColor,
|
||
kSixteenBit,
|
||
kThirtytwoBit,
|
||
kNumColorSpaces
|
||
};
|
||
|
||
enum DefaultColors {
|
||
kDefaultBGColorRed = 192,
|
||
kDefaultBGColorGreen = 192,
|
||
kDefaultBGColorBlue = 192
|
||
};
|
||
|
||
|
||
typedef struct PictureGWorldState {
|
||
GWorldPtr gworld;
|
||
RGBColor transparentColor;
|
||
Int32 transparentIndex;
|
||
CTabHandle ctab;
|
||
Int32 imageHeight;
|
||
Int32 bandHeight;
|
||
NS_PixMap * image;
|
||
NS_PixMap * mask;
|
||
} PictureGWorldState;
|
||
|
||
/* BRAIN DAMAGE: This scary thing came from the old code */
|
||
#define IL_ICON_OFFSET 300
|
||
|
||
/*
|
||
* Internal function prototypes
|
||
*/
|
||
|
||
static OSErr AllocatePixMap ( IL_Pixmap * il_pixmap, jint width, jint height,
|
||
NS_PixMap * fe_pixmap, CTabHandle ctab );
|
||
|
||
static ColorSpaceIndex GetNextBestColorSpaceIndex ( ColorSpaceIndex index );
|
||
static ColorSpaceIndex ConvertColorSpaceToIndex ( IL_ColorSpace * color_space );
|
||
static ColorSpaceIndex ConvertDepthToColorSpaceIndex ( Int32 depth, Boolean grayScale );
|
||
static IL_ColorSpace * GetColorSpace ( MWContext * context, ColorSpaceIndex space_index, CTabHandle * color_table );
|
||
static CTabHandle ConvertColorSpaceToColorTable ( IL_ColorSpace * color_space );
|
||
static void SetColorSpaceTransparentColor ( IL_ColorSpace * color_space, Uint8 red, Uint8 green, Uint8 blue );
|
||
|
||
static OSErr CreatePictureGWorld ( IL_Pixmap * image, IL_Pixmap * mask, PictureGWorldState * state );
|
||
static void TeardownPictureGWorld ( PictureGWorldState * state );
|
||
static void CopyPicture ( PictureGWorldState * state );
|
||
static void CreateUniqueTransparentColor ( IL_Pixmap * image, PictureGWorldState * state );
|
||
static Boolean FindColorInCTable ( CTabHandle ctab, Uint32 skipIndex, RGBColor * rgb );
|
||
|
||
static void LockPixmapBuffer ( IL_Pixmap * pixmap );
|
||
static void UnlockPixmapBuffer ( IL_Pixmap * pixmap );
|
||
|
||
static CIconHandle GetIconHandle ( jint iconID );
|
||
static IL_GroupContext * CreateImageGroupContext ( MWContext * context );
|
||
|
||
|
||
/*
|
||
* Globals
|
||
*/
|
||
|
||
IL_ColorSpace * gColorSpaces[ kNumColorSpaces ] = { NULL, NULL, NULL, NULL, NULL };
|
||
CTabHandle gColorTables[ kNumColorSpaces ] = { NULL, NULL, NULL, NULL, NULL };
|
||
CTabHandle gOneBitTable = NULL;
|
||
|
||
#if TRACK_IMAGE_CACHE_SIZE
|
||
static Uint32 gCacheSize = 0;
|
||
static Uint32 gMaxCacheSize = 0;
|
||
#endif
|
||
|
||
#pragma mark --- IMAGE LIB CALLBACKS ---
|
||
|
||
/*
|
||
* Callback procs for the ImageLib
|
||
*/
|
||
|
||
JMC_PUBLIC_API(void*)
|
||
_IMGCB_getBackwardCompatibleInterface(struct IMGCB* /*self*/, const JMCInterfaceID* /*iid*/,
|
||
JMCException* */*exception*/)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
JMC_PUBLIC_API(void)
|
||
_IMGCB_init(struct IMGCB* /*self*/, JMCException* */*exception*/)
|
||
{
|
||
|
||
}
|
||
|
||
JMC_PUBLIC_API(void)
|
||
_IMGCB_NewPixmap(struct IMGCB* /*self*/, jint /*op*/, void* a, jint width, jint height, IL_Pixmap* pixmap,
|
||
IL_Pixmap* mask)
|
||
{
|
||
MWContext * context = (MWContext *) a;
|
||
NS_PixMap * fe_pixmap;
|
||
NS_PixMap * fe_mask;
|
||
OSErr err;
|
||
ColorSpaceIndex space_index;
|
||
ColorSpaceIndex image_index;
|
||
CTabHandle ctab;
|
||
IL_ColorSpace * color_space;
|
||
FreeMemoryStats freeMemory;
|
||
Uint32 freeTempMemory;
|
||
|
||
XP_ASSERT(pixmap != NULL);
|
||
|
||
ctab = NULL;
|
||
|
||
/* BRAIN DAMAGE - for now let the image lib do any scaling */
|
||
pixmap->header.width = width;
|
||
pixmap->header.height = height;
|
||
|
||
fe_pixmap = XP_NEW(NS_PixMap);
|
||
XP_ASSERT(fe_pixmap != NULL);
|
||
if ( fe_pixmap != NULL )
|
||
{
|
||
fe_pixmap->lock_count = 0;
|
||
fe_pixmap->image_buffer = 0L;
|
||
fe_pixmap->buffer_handle = 0L;
|
||
fe_pixmap->tiled = false;
|
||
|
||
color_space = pixmap->header.color_space;
|
||
|
||
/*
|
||
* if this pixmap's color space is of lower resolution than our current
|
||
* screen depth, then use the images, otherwise use the screen depth.
|
||
*/
|
||
|
||
space_index = ConvertDepthToColorSpaceIndex( 0, false );
|
||
|
||
if ( color_space != NULL )
|
||
{
|
||
image_index = ConvertColorSpaceToIndex ( color_space );
|
||
|
||
if ( image_index < space_index )
|
||
{
|
||
/*
|
||
* This image appears to be at a lower colour depth, but it may have a
|
||
* unique color table, so we may want to allocate it at a deeper depth.
|
||
* So, if we're getting short on memory, decrease the depth, otherwise
|
||
* use the default depth.
|
||
*
|
||
* We don't really base this on the size of the image as we ideally want to
|
||
* start decreasing the size of images, before we run out of memory (which
|
||
* other parts of the browser may not handle as well).
|
||
*/
|
||
memtotal ( 1024, &freeMemory );
|
||
freeTempMemory = TempFreeMem();
|
||
|
||
if (( freeMemory.totalFreeBytes < 0x10000 ) || ( freeTempMemory < 0x40000 ))
|
||
{
|
||
space_index = image_index;
|
||
}
|
||
}
|
||
|
||
/* we don't want this color space anymore */
|
||
IL_ReleaseColorSpace ( color_space );
|
||
pixmap->header.color_space = NULL;
|
||
color_space = NULL;
|
||
}
|
||
|
||
/*
|
||
* Try to allocate the pixmap at the ideal colour depth. If we can't, then keep
|
||
* downgrading the colour resolution until we can.
|
||
*/
|
||
do
|
||
{
|
||
color_space = GetColorSpace ( context, space_index, &ctab );
|
||
|
||
if ( color_space != NULL && ctab != NULL )
|
||
{
|
||
pixmap->header.color_space = color_space;
|
||
err = AllocatePixMap ( pixmap, width, height, fe_pixmap, ctab );
|
||
if ( err == noErr )
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
space_index = GetNextBestColorSpaceIndex ( space_index );
|
||
}
|
||
while ( space_index < kNumColorSpaces );
|
||
|
||
if ( err == noErr )
|
||
{
|
||
/* add a ref to this color space */
|
||
IL_AddRefToColorSpace ( color_space );
|
||
XP_ASSERT(pixmap->header.color_space != NULL);
|
||
}
|
||
else
|
||
{
|
||
pixmap->header.color_space = NULL;
|
||
XP_DELETE(fe_pixmap);
|
||
fe_pixmap = NULL;
|
||
}
|
||
}
|
||
|
||
pixmap->client_data = fe_pixmap;
|
||
|
||
if ( mask != NULL )
|
||
{
|
||
/* BRAIN DAMAGE - for now let the image lib do any scaling */
|
||
mask->header.width = width;
|
||
mask->header.height = height;
|
||
|
||
fe_mask = XP_NEW(NS_PixMap);
|
||
XP_ASSERT(fe_mask != NULL);
|
||
if ( fe_mask != NULL )
|
||
{
|
||
fe_mask->lock_count = 0;
|
||
fe_mask->image_buffer = NULL;
|
||
fe_mask->buffer_handle = NULL;
|
||
fe_mask->tiled = false;
|
||
|
||
err = AllocatePixMap ( mask, width, height, fe_mask, gOneBitTable );
|
||
if ( err == noErr )
|
||
{
|
||
mask->client_data = fe_mask;
|
||
}
|
||
else
|
||
{
|
||
XP_DELETE(fe_mask);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* If the image has a transparent color, make sure to update it from the current context
|
||
*/
|
||
if ( pixmap->header.transparent_pixel != NULL )
|
||
{
|
||
*pixmap->header.transparent_pixel = *context->transparent_pixel;
|
||
}
|
||
|
||
XP_ASSERT(pixmap->header.color_space->pixmap_depth != 0 );
|
||
}
|
||
|
||
JMC_PUBLIC_API(void)
|
||
_IMGCB_UpdatePixmap(struct IMGCB* /*self*/, jint /*op*/, void* /*a*/, IL_Pixmap* /*pixmap*/, jint /*x_offset*/,
|
||
jint /*y_offset*/, jint /*width*/, jint /*height*/)
|
||
{
|
||
}
|
||
|
||
JMC_PUBLIC_API(void)
|
||
_IMGCB_ControlPixmapBits(struct IMGCB* /*self*/, jint /*op*/, void* a, IL_Pixmap* pixmap, IL_PixmapControl message)
|
||
{
|
||
MWContext * context = (MWContext *) a;
|
||
NS_PixMap * fe_pixmap = NULL;
|
||
|
||
XP_ASSERT(pixmap != NULL);
|
||
|
||
fe_pixmap = (NS_PixMap *) pixmap->client_data;
|
||
XP_ASSERT(fe_pixmap != NULL);
|
||
|
||
if ( fe_pixmap != NULL )
|
||
{
|
||
switch ( message )
|
||
{
|
||
case IL_LOCK_BITS:
|
||
LockPixmapBuffer ( pixmap );
|
||
break;
|
||
|
||
case IL_UNLOCK_BITS:
|
||
UnlockPixmapBuffer ( pixmap );
|
||
break;
|
||
|
||
case IL_RELEASE_BITS:
|
||
// the image is totally done loading, so tell the cache that the context associated
|
||
// with this image will go away and we know we can just give out the bits to anyone
|
||
// that asks in the future.
|
||
if ( context->type == MWContextIcon )
|
||
gImageCache().ContextFinished ( context );
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
JMC_PUBLIC_API(void)
|
||
_IMGCB_DestroyPixmap(struct IMGCB* /*self*/, jint /*op*/, void* a, IL_Pixmap* pixmap)
|
||
{
|
||
MWContext * context = (MWContext *) a;
|
||
NS_PixMap * fe_pixmap = NULL;
|
||
|
||
XP_ASSERT(pixmap != NULL);
|
||
fe_pixmap = (NS_PixMap *) pixmap->client_data;
|
||
|
||
if ( fe_pixmap != NULL )
|
||
{
|
||
#if TRACK_IMAGE_CACHE_SIZE
|
||
gCacheSize -= pixmap->header.widthBytes * pixmap->header.height;
|
||
#endif
|
||
DestroyFEPixmap ( fe_pixmap );
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// _IMGCB_DisplayPixmap
|
||
//
|
||
// Called from imagelib when the image is ready to draw any portion (one frame of an animation, parital
|
||
// progressive decoding, etc). Also called from the compositor to re-blit an already loaded image when
|
||
// scrolling or refreshing the view.
|
||
//
|
||
// We hook into this as well for drawing gifs/jpegs in the FE for icons because when we get this
|
||
// call, we know that the image has been loaded and is ready to display.
|
||
//
|
||
JMC_PUBLIC_API(void)
|
||
_IMGCB_DisplayPixmap(struct IMGCB* /*self*/, jint /*op*/, void* inContext, IL_Pixmap* image, IL_Pixmap* mask,
|
||
jint x, jint y, jint x_offset, jint y_offset, jint width, jint height, jint req_w,
|
||
jint req_h)
|
||
{
|
||
MWContext * context = static_cast<MWContext *>(inContext);
|
||
NS_PixMap * fe_pixmap;
|
||
NS_PixMap * fe_mask;
|
||
SPoint32 topLeftImage;
|
||
StColorState saveColorState;
|
||
Point topLeft;
|
||
int32 x_origin;
|
||
int32 y_origin;
|
||
Boolean canCopyMask;
|
||
OSErr err;
|
||
DrawingState state;
|
||
|
||
// This routine was written assuming that we are drawing into a CHTMLView (why else would you be
|
||
// drawing gifs?) However, we also want to hook in here so we know when images we want to draw in the
|
||
// chrome have loaded and are ready to display. If the context type is right (an image context),
|
||
// let it handle the work and return.
|
||
if ( context->type == MWContextIcon ) {
|
||
CIconContext* iconContext = dynamic_cast<CIconContext*>(ExtractNSContext(context));
|
||
if ( iconContext )
|
||
iconContext->DrawImage(image, mask);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// We're not given an image context so we draw like normal into an HTML view.
|
||
//
|
||
CHTMLView* theHTMLView = ExtractHyperView(context);
|
||
if ( image != NULL && theHTMLView && theHTMLView->FocusDraw())
|
||
{
|
||
StColorPenState::Normalize();
|
||
|
||
theHTMLView->GetLayerOrigin ( &x_origin, &y_origin );
|
||
|
||
/*
|
||
* Can we use copymask for transparency? QuickDraw can't save Masks to Pictures nor
|
||
* can we print them.
|
||
*/
|
||
canCopyMask = ( context->type != MWContextPrint ) && ( qd.thePort->picSave == NULL );
|
||
|
||
fe_pixmap = (NS_PixMap *) image->client_data;
|
||
fe_mask = mask != NULL ? (NS_PixMap *) mask->client_data : NULL;
|
||
|
||
if ( fe_pixmap != NULL )
|
||
{
|
||
// pinkerton
|
||
// we need a way to prevent images in the HTML area from drawing over the
|
||
// popdown treeView if it is visible. Clip it out.
|
||
StClipRgnState savedClip;
|
||
CBrowserWindow::ClipOutPopdown(theHTMLView);
|
||
|
||
topLeftImage.h = x + x_offset + x_origin;
|
||
topLeftImage.v = y + y_offset + y_origin;
|
||
|
||
theHTMLView->ImageToLocalPoint( topLeftImage, topLeft );
|
||
|
||
err = PreparePixmapForDrawing ( image, mask, canCopyMask, &state );
|
||
if ( err == noErr )
|
||
{
|
||
if ( fe_pixmap->tiled != false )
|
||
{
|
||
DrawTiledImage ( &state, topLeft, x_offset, y_offset, width, height );
|
||
}
|
||
else
|
||
{
|
||
DrawScaledImage ( &state, topLeft, x_offset, y_offset, width, height );
|
||
}
|
||
|
||
DoneDrawingPixmap ( image, mask, &state );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
JMC_PUBLIC_API(void)
|
||
_IMGCB_DisplayIcon(struct IMGCB* /*self*/, jint /*op*/, void* a, jint x, jint y, jint icon_number)
|
||
{
|
||
MWContext * context = (MWContext *) a;
|
||
Rect icon_dest;
|
||
CIconHandle ic;
|
||
PixMap * pixmap;
|
||
int32 x_origin;
|
||
int32 y_origin;
|
||
SPoint32 topLeftImage;
|
||
Point topLeft;
|
||
|
||
CHTMLView* theHTMLView = ExtractHyperView(context);
|
||
if (theHTMLView == NULL)
|
||
return;
|
||
|
||
ic = GetIconHandle ( icon_number );
|
||
if ( ic != NULL )
|
||
{
|
||
pixmap = &(*ic)->iconPMap;
|
||
|
||
// Convert from layer-relative coordinates to local coordinates
|
||
theHTMLView->GetLayerOrigin ( &x_origin, &y_origin );
|
||
topLeftImage.h = x + x_origin;
|
||
topLeftImage.v = y + y_origin;
|
||
theHTMLView->ImageToLocalPoint( topLeftImage, topLeft );
|
||
|
||
icon_dest.left = topLeft.h;
|
||
icon_dest.top = topLeft.v;
|
||
icon_dest.right = icon_dest.left + ( pixmap->bounds.right - pixmap->bounds.left );
|
||
icon_dest.bottom = icon_dest.top + ( pixmap->bounds.bottom - pixmap->bounds.top );
|
||
|
||
::PlotCIconHandle( &icon_dest, atAbsoluteCenter, ttNone, ic );
|
||
CIconList::ReturnIcon ( ic );
|
||
}
|
||
}
|
||
|
||
JMC_PUBLIC_API(void)
|
||
_IMGCB_GetIconDimensions(struct IMGCB* /*self*/, jint /*op*/, void* a, int* width, int* height, jint icon_number)
|
||
{
|
||
MWContext * context = (MWContext *) a;
|
||
CIconHandle ic;
|
||
PixMap * pixmap;
|
||
|
||
ic = GetIconHandle ( icon_number );
|
||
if ( ic == NULL )
|
||
{
|
||
*width = 16;
|
||
*height = 16;
|
||
}
|
||
else
|
||
{
|
||
pixmap = &(*ic)->iconPMap;
|
||
*width = pixmap->bounds.right - pixmap->bounds.left;
|
||
*height = pixmap->bounds.bottom - pixmap->bounds.top;
|
||
|
||
CIconList::ReturnIcon ( ic );
|
||
}
|
||
}
|
||
|
||
#pragma mark --- PUBLIC FUNCTIONS ---
|
||
|
||
EClickKind FindImagePart (
|
||
MWContext * context,
|
||
LO_ImageStruct * image,
|
||
SPoint32 * where,
|
||
cstring * url,
|
||
cstring * target,
|
||
LO_AnchorData * & anchor )
|
||
{
|
||
SPoint32 local_point;
|
||
EClickKind click;
|
||
int iconWidth;
|
||
int iconHeight;
|
||
|
||
click = eNone;
|
||
|
||
/*
|
||
* Convert the document coordinate to an image local coordinate
|
||
*/
|
||
local_point.h = where->h - ( image->x + image->x_offset + image->border_width );
|
||
local_point.v = where->v - ( image->y + image->y_offset + image->border_width );
|
||
|
||
if (image->anchor_href)
|
||
{
|
||
PA_LOCK(*url, char*, (char*)image->anchor_href->anchor);
|
||
PA_UNLOCK(loImage->anchor_href->anchor );
|
||
PA_LOCK(*target, char*, (char*)image->anchor_href->target);
|
||
PA_UNLOCK(loImage->anchor_href->target );
|
||
}
|
||
|
||
if ( image->image_attr->attrmask & LO_ATTR_ISFORM )
|
||
{
|
||
char s[100];
|
||
|
||
// <20> form image
|
||
click = eImageForm;
|
||
char * printString = GetCString(IMAGE_SUBMIT_FORM);
|
||
sprintf (s, (char*)printString, local_point.h, local_point.v); // "Submit form:%d,%d"
|
||
*url = s;
|
||
}
|
||
else if ( image->image_attr->usemap_name != NULL )
|
||
{
|
||
// <20> client-side image maps
|
||
anchor = LO_MapXYToAreaAnchor( context, image, local_point.h, local_point.v );
|
||
if ( anchor )
|
||
{
|
||
PA_LOCK( *url, char*, (char*)anchor->anchor );
|
||
PA_UNLOCK( anchor->anchor );
|
||
PA_LOCK( *target, char*, (char*)anchor->target );
|
||
PA_UNLOCK( anchor->target );
|
||
click = eImageAnchor;
|
||
}
|
||
else
|
||
click = eNone;
|
||
}
|
||
else if ( (image->image_attr->attrmask & LO_ATTR_ISMAP) && image->anchor_href )
|
||
{
|
||
// <20><>ISMAP
|
||
click = eImageIsmap;
|
||
char s[50];
|
||
url->truncAt( '?' );
|
||
url->truncAt( '#' );
|
||
sprintf( s, "?%d,%d", local_point.h, local_point.v );
|
||
*url += s;
|
||
}
|
||
else if ( IsImageComplete ( image ) == FALSE )
|
||
{
|
||
// Did we click in an icon or an alt text?
|
||
// BRAIN DAMAGE
|
||
#ifdef OLD_IMAGE_LIB
|
||
// <20> delayed image
|
||
if (((IconProxy*)imageProxy)->ClickInIcon(mImageWhere.h, mImageWhere.v))
|
||
theKind = eImageIcon;
|
||
else if ( ((IconProxy*)imageProxy)->ClickInAltText(mImageWhere.h, mImageWhere.v) && loImage->anchor_href)
|
||
theKind = eImageAltText;
|
||
else
|
||
theKind = eNone;
|
||
|
||
#endif
|
||
/* is the image an icon? */
|
||
if ( image->is_icon )
|
||
{
|
||
IL_GetIconDimensions ( context->img_cx, image->icon_number, &iconWidth, &iconHeight );
|
||
|
||
/*
|
||
* If the image has no anchor or the click is inside the icon, mark the click as being
|
||
* the icon
|
||
*/
|
||
if ( ( ( local_point.h < iconWidth ) && ( local_point.v < iconHeight ) )
|
||
|| ( image->anchor_href == NULL ) )
|
||
{
|
||
click = eImageIcon;
|
||
}
|
||
else
|
||
{
|
||
click = eImageAltText;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// <20> plain image with an anchor
|
||
if ( image->anchor_href )
|
||
{
|
||
click = eImageAnchor;
|
||
}
|
||
}
|
||
|
||
return click;
|
||
}
|
||
|
||
BOOL IsImageComplete (
|
||
LO_ImageStruct * image )
|
||
{
|
||
return ((image->image_status == IL_IMAGE_COMPLETE) ||
|
||
(image->image_status == IL_FRAME_COMPLETE));
|
||
}
|
||
|
||
// a hack to get the URL of the image
|
||
// if the image is using that reconnect hack, we get the URL of the document
|
||
// instead of the URL of the image
|
||
cstring GetURLFromImageElement(
|
||
CBrowserContext * inOwningContext,
|
||
LO_ImageStruct * inElement)
|
||
{
|
||
cstring retString;
|
||
cstring urlString = (char*)inElement->image_url;
|
||
|
||
// <20><>handle anything starting with "internal-"
|
||
if (IsInternalTypeLink(urlString))
|
||
{
|
||
// <20> handle special mail & news reconnects for internal decoded images
|
||
// that start "internal-external-reconnect:" followed by the original
|
||
// URL
|
||
if (IsMailNewsReconnect(urlString))
|
||
retString = &urlString[XP_STRLEN(reconnectHack) + 1];
|
||
// <20> handle "internal-external-reconnect" case when we don't have an
|
||
// associated anchor_href
|
||
else if (!inElement->anchor_href && IsInternalImage(urlString))
|
||
{
|
||
retString = inOwningContext->GetCurrentURL();
|
||
}
|
||
else
|
||
// <20> jwz's hack upon a hack!
|
||
// That is, if there is both an image and an anchor, and the address of
|
||
// the image is internal-external-reconnect, use the anchor for the image,
|
||
// and pretend there was no anchor.
|
||
|
||
// A side effect of the way I did this is that internal-external-reconnect
|
||
// images which have HREF (mail and news articles) are clickable links to
|
||
// themselves; we could special case this, but it doesn't hurt anything,
|
||
// and could even be mistaken for intentional, so who cares ( JWZ wrote
|
||
// that origially and I don't think it applies to the actual logic here - tgm ).
|
||
{
|
||
retString = (char*)inElement->anchor_href->anchor;
|
||
}
|
||
}
|
||
else
|
||
retString = urlString;
|
||
|
||
return retString;
|
||
}
|
||
|
||
PicHandle ConvertImageElementToPICT(
|
||
LO_ImageStruct * inElement)
|
||
{
|
||
PicHandle pic;
|
||
OpenCPicParams picParams;
|
||
IL_Pixmap * image;
|
||
IL_Pixmap * mask;
|
||
DrawingState state;
|
||
OSErr err;
|
||
NS_PixMap * fe_pixmap;
|
||
Rect dstRect;
|
||
PictureGWorldState picState;
|
||
GrafPtr oldPort;
|
||
CGrafPort cport;
|
||
|
||
err = noErr;
|
||
|
||
/*
|
||
* We render into our own port so that we don't need to play with other origin/port setting
|
||
* fun
|
||
*/
|
||
GetPort ( &oldPort );
|
||
OpenCPort ( &cport );
|
||
|
||
if ( QDError() != noErr )
|
||
return NULL;
|
||
|
||
SetPort ( (GrafPtr) &cport );
|
||
|
||
SetRect ( &picParams.srcRect, 0, 0, inElement->width, inElement->height );
|
||
picParams.hRes = 72L << 16;
|
||
picParams.vRes = 72L << 16;
|
||
picParams.version = 0;
|
||
picParams.reserved1 = 0;
|
||
picParams.reserved2 = 0;
|
||
|
||
image = IL_GetImagePixmap ( inElement->image_req );
|
||
mask = IL_GetMaskPixmap ( inElement->image_req );
|
||
|
||
if ( image == NULL )
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
pic = OpenCPicture ( &picParams );
|
||
if ( pic != NULL )
|
||
{
|
||
/* if we have no mask, then we're just dandy */
|
||
if ( mask == NULL )
|
||
{
|
||
err = PreparePixmapForDrawing ( image, mask, false, &state );
|
||
if ( err == noErr )
|
||
{
|
||
fe_pixmap = state.pixmap;
|
||
SetRect ( &dstRect, 0, 0, inElement->width, inElement->height );
|
||
|
||
CopyBits ( (BitMap *) &fe_pixmap->pixmap, &qd.thePort->portBits,
|
||
&fe_pixmap->pixmap.bounds, &dstRect, srcCopy, NULL );
|
||
|
||
DoneDrawingPixmap ( image, mask, &state );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* We have transparency, so we suck. What we basically do is allocate an offscreen,
|
||
* render a unique color into the background, copymask the image over it and then
|
||
* copy that image into a picture.
|
||
*
|
||
* This particularly sucks for grayscale images as we need to make sure that our
|
||
* transparent color is unique.
|
||
*
|
||
* Thus, for indexed images, we create a color table that contains our reserved
|
||
* entry as a reserved index. We then CopyBits the original image onto it. Then,
|
||
* we set our reserved entry to a magic value and do a final CopyBits with transparent
|
||
* mode into the picture.
|
||
*/
|
||
|
||
err = CreatePictureGWorld ( image, mask, &picState );
|
||
if ( err == noErr )
|
||
{
|
||
LockPixmapBuffer ( image );
|
||
LockPixmapBuffer ( mask );
|
||
|
||
CopyPicture ( &picState );
|
||
|
||
UnlockPixmapBuffer ( image );
|
||
UnlockPixmapBuffer ( mask );
|
||
|
||
TeardownPictureGWorld ( &picState );
|
||
}
|
||
}
|
||
|
||
ClosePicture();
|
||
}
|
||
|
||
SetPort ( oldPort );
|
||
CloseCPort ( &cport );
|
||
|
||
/*
|
||
* If we got an error, don't return a bad picture
|
||
*/
|
||
if ( err != noErr )
|
||
{
|
||
KillPicture ( pic );
|
||
pic = NULL;
|
||
}
|
||
|
||
return pic;
|
||
}
|
||
|
||
void CreateImageContext (
|
||
MWContext * context )
|
||
{
|
||
IL_DisplayData data;
|
||
ColorSpaceIndex space_index;
|
||
|
||
/*
|
||
* Create a new image context.
|
||
*/
|
||
context->img_cx = CreateImageGroupContext ( context );
|
||
Assert_(context->img_cx != NULL);
|
||
|
||
if ( context->img_cx != NULL )
|
||
{
|
||
/*
|
||
* Set the context's color space to be our best one
|
||
*/
|
||
space_index = ConvertDepthToColorSpaceIndex( 0, false );
|
||
context->color_space = GetColorSpace ( context, space_index, NULL );
|
||
PR_ASSERT(context->color_space != NULL);
|
||
|
||
if ( context->type == MWContextPrint )
|
||
{
|
||
data.display_type = IL_Printer;
|
||
}
|
||
else
|
||
{
|
||
data.display_type = IL_Console;
|
||
}
|
||
|
||
data.color_space = context->color_space;
|
||
data.progressive_display = CPrefs::GetBoolean(CPrefs::DisplayWhileLoading) ?
|
||
PR_TRUE : PR_FALSE;
|
||
data.dither_mode = IL_Auto;
|
||
|
||
IL_SetDisplayMode ( context->img_cx,
|
||
IL_PROGRESSIVE_DISPLAY | IL_DITHER_MODE | IL_COLOR_SPACE | IL_DISPLAY_TYPE, &data );
|
||
}
|
||
|
||
/*
|
||
* Get a standard one bit table for masks
|
||
*/
|
||
if ( gOneBitTable == NULL )
|
||
{
|
||
gOneBitTable = GetCTable ( 1 );
|
||
}
|
||
|
||
/*
|
||
* Set the default background color
|
||
*/
|
||
SetImageContextBackgroundColor ( context, kDefaultBGColorRed, kDefaultBGColorGreen, kDefaultBGColorBlue );
|
||
}
|
||
|
||
void DestroyImageContext (
|
||
MWContext * context )
|
||
{
|
||
if ( context->color_space != NULL )
|
||
{
|
||
context->color_space = NULL;
|
||
}
|
||
|
||
if ( context->img_cx != NULL )
|
||
{
|
||
IL_DestroyGroupContext ( context->img_cx );
|
||
context->img_cx = NULL;
|
||
}
|
||
}
|
||
|
||
void SetImageContextBackgroundColor (
|
||
MWContext * context,
|
||
Uint8 red,
|
||
Uint8 green,
|
||
Uint8 blue)
|
||
{
|
||
IL_ColorSpace * color_space;
|
||
IL_IRGB * trans_pixel;
|
||
Uint8 count;
|
||
|
||
color_space = context->color_space;
|
||
trans_pixel = context->transparent_pixel;
|
||
|
||
if ( trans_pixel == NULL )
|
||
{
|
||
trans_pixel = context->transparent_pixel = XP_NEW_ZAP(IL_IRGB);
|
||
if ( trans_pixel == NULL )
|
||
return;
|
||
}
|
||
|
||
/* Set the color of the transparent pixel. */
|
||
trans_pixel->red = red;
|
||
trans_pixel->green = green;
|
||
trans_pixel->blue = blue;
|
||
if ( color_space->type == NI_PseudoColor )
|
||
{
|
||
trans_pixel->index = color_space->cmap.num_colors - 1;
|
||
}
|
||
|
||
/*
|
||
* For PseudoColor color spaces, we must also update our color map with this
|
||
* new color. Our transparent/background color is always at the end of the color map.
|
||
*/
|
||
XP_ASSERT(color_space);
|
||
SetColorSpaceTransparentColor ( color_space, red, green, blue );
|
||
|
||
/*
|
||
* Now set the transparent color for any other cached spaces
|
||
*/
|
||
for ( count = 0; count < kNumColorSpaces; ++count )
|
||
{
|
||
if ( gColorSpaces[ count ] != NULL )
|
||
{
|
||
SetColorSpaceTransparentColor ( gColorSpaces[ count ], red, green, blue );
|
||
}
|
||
}
|
||
}
|
||
|
||
GDHandle GetDeepestDevice (
|
||
void )
|
||
{
|
||
GDHandle deepest = GetMainDevice();
|
||
short depth = GetDepth (deepest);
|
||
|
||
for (GDHandle current = GetDeviceList(); current; current = GetNextDevice (current)) {
|
||
if (UDrawingUtils::IsActiveScreenDevice (current)) {
|
||
short curDepth = GetDepth (current);
|
||
if (depth < curDepth) {
|
||
depth = curDepth;
|
||
deepest = current;
|
||
}
|
||
}
|
||
}
|
||
|
||
return deepest;
|
||
}
|
||
|
||
Boolean VerifyDisplayContextColorSpace (
|
||
MWContext * context )
|
||
{
|
||
ColorSpaceIndex screen_index;
|
||
ColorSpaceIndex current_index;
|
||
IL_ColorSpace * color_space;
|
||
IL_DisplayData data;
|
||
Boolean mustReload;
|
||
uint32 cacheSize;
|
||
|
||
mustReload = false;
|
||
|
||
/*
|
||
* Make sure that the current display color space matches the context's color space
|
||
*/
|
||
screen_index = ConvertDepthToColorSpaceIndex ( 0, false );
|
||
current_index = ConvertColorSpaceToIndex ( context->color_space );
|
||
if ( screen_index != current_index )
|
||
{
|
||
/* the color space of the screen has changed, update our display context */
|
||
color_space = GetColorSpace ( context, screen_index, NULL );
|
||
if ( color_space != NULL )
|
||
{
|
||
context->color_space = color_space;
|
||
|
||
data.color_space = color_space;
|
||
IL_SetDisplayMode ( context->img_cx, IL_COLOR_SPACE, &data );
|
||
|
||
SetColorSpaceTransparentColor ( color_space, context->transparent_pixel->red,
|
||
context->transparent_pixel->green, context->transparent_pixel->blue );
|
||
|
||
/* if this new space is deeper than the old, we should reload */
|
||
mustReload = screen_index > current_index;
|
||
|
||
/* and if we do need to reload, we should flush the image cache as well */
|
||
/* it's up to our caller to decide if they want to reload the current page */
|
||
if ( mustReload )
|
||
{
|
||
/* the only way to do this is to set the cache to 0 size and then restore it */
|
||
cacheSize = IL_GetCacheSize();
|
||
IL_SetCacheSize ( 0 );
|
||
IL_SetCacheSize ( cacheSize );
|
||
}
|
||
}
|
||
}
|
||
|
||
return mustReload;
|
||
}
|
||
|
||
|
||
void DrawScaledImage ( DrawingState * state, Point topLeft, jint x_offset,
|
||
jint y_offset, jint width, jint height )
|
||
{
|
||
Rect srcRect;
|
||
Rect * maskRectPtr;
|
||
Rect dstRect;
|
||
PixMap * maskPtr;
|
||
NS_PixMap * fe_pixmap;
|
||
NS_PixMap * fe_mask;
|
||
|
||
fe_pixmap = state->pixmap;
|
||
fe_mask = state->mask;
|
||
|
||
if ( fe_mask != NULL )
|
||
{
|
||
maskPtr = &fe_mask->pixmap;
|
||
maskRectPtr = &maskPtr->bounds;
|
||
}
|
||
else
|
||
{
|
||
maskPtr = NULL;
|
||
maskRectPtr = NULL;
|
||
}
|
||
|
||
srcRect.left = x_offset;
|
||
srcRect.top = y_offset;
|
||
srcRect.right = x_offset + width;
|
||
srcRect.bottom = y_offset + height;
|
||
|
||
/*
|
||
* Clip src Rect to the image.
|
||
*/
|
||
if ( fe_pixmap->pixmap.bounds.right < srcRect.right )
|
||
{
|
||
srcRect.right = fe_pixmap->pixmap.bounds.right;
|
||
}
|
||
if ( fe_pixmap->pixmap.bounds.bottom < srcRect.bottom )
|
||
{
|
||
srcRect.bottom = fe_pixmap->pixmap.bounds.bottom;
|
||
}
|
||
|
||
SetRect ( &dstRect, topLeft.h, topLeft.v, topLeft.h + width, topLeft.v + height );
|
||
|
||
if ( maskPtr == NULL )
|
||
{
|
||
CopyBits ( (BitMap *) &fe_pixmap->pixmap, &qd.thePort->portBits, &srcRect,
|
||
&dstRect, state->copyMode, NULL );
|
||
}
|
||
else
|
||
{
|
||
CopyMask ( (BitMap *) &fe_pixmap->pixmap, (BitMap *) maskPtr, &qd.thePort->portBits,
|
||
&srcRect, maskRectPtr, &dstRect );
|
||
}
|
||
}
|
||
|
||
void DrawTiledImage ( DrawingState * state, Point topLeft, jint x_offset,
|
||
jint y_offset, jint width, jint height )
|
||
{
|
||
|
||
Rect srcRect;
|
||
Rect * maskRectPtr;
|
||
Rect dstRect;
|
||
PixMap * maskPtr;
|
||
int32 right_clip;
|
||
int32 bottom_clip;
|
||
int32 left;
|
||
int32 top;
|
||
int32 img_width;
|
||
int32 img_height;
|
||
int32 tile_width;
|
||
int32 tile_height;
|
||
int32 src_x_offset;
|
||
int32 src_y_offset;
|
||
NS_PixMap * fe_pixmap;
|
||
NS_PixMap * fe_mask;
|
||
|
||
fe_pixmap = state->pixmap;
|
||
fe_mask = state->mask;
|
||
|
||
if ( fe_mask != NULL )
|
||
{
|
||
maskPtr = &fe_mask->pixmap;
|
||
maskRectPtr = &srcRect;
|
||
}
|
||
else
|
||
{
|
||
maskPtr = NULL;
|
||
maskRectPtr = NULL;
|
||
}
|
||
|
||
img_width = fe_pixmap->pixmap.bounds.right - fe_pixmap->pixmap.bounds.left;
|
||
img_height = fe_pixmap->pixmap.bounds.bottom - fe_pixmap->pixmap.bounds.top;
|
||
|
||
right_clip = topLeft.h + width;
|
||
bottom_clip = topLeft.v + height;
|
||
|
||
/* the first row may be shorter */
|
||
tile_height = img_height - ( y_offset % img_height );
|
||
|
||
/*
|
||
* Walk through all the tiles in dst space
|
||
*/
|
||
|
||
top = topLeft.v;
|
||
src_y_offset = y_offset % img_height;
|
||
|
||
while ( top < bottom_clip )
|
||
{
|
||
left = topLeft.h;
|
||
tile_width = img_width - ( x_offset % img_width );
|
||
src_x_offset = x_offset % img_width;
|
||
|
||
while ( left < right_clip )
|
||
{
|
||
dstRect.left = left;
|
||
dstRect.top = top;
|
||
dstRect.right = left + tile_width;
|
||
dstRect.bottom = top + tile_height;
|
||
|
||
srcRect.left = src_x_offset;
|
||
srcRect.top = src_y_offset;
|
||
srcRect.right = src_x_offset + tile_width;
|
||
srcRect.bottom = src_y_offset + tile_height;
|
||
|
||
if ( maskPtr == NULL )
|
||
{
|
||
CopyBits ( (BitMap *) &fe_pixmap->pixmap, &qd.thePort->portBits, &srcRect,
|
||
&dstRect, state->copyMode, NULL );
|
||
}
|
||
else
|
||
{
|
||
CopyMask ( (BitMap *) &fe_pixmap->pixmap, (BitMap *) maskPtr, &qd.thePort->portBits,
|
||
&srcRect, maskRectPtr, &dstRect );
|
||
}
|
||
|
||
/*
|
||
* Bump to the next column and be sure to clip if it's the last one
|
||
*/
|
||
left += tile_width;
|
||
tile_width = img_width;
|
||
if ( left + tile_width > right_clip )
|
||
{
|
||
tile_width = right_clip - left;
|
||
}
|
||
src_x_offset = 0;
|
||
}
|
||
|
||
/*
|
||
* Bump to the next row and be sure to clip if it's the last one
|
||
*/
|
||
top += tile_height;
|
||
tile_height = img_height;
|
||
if ( top + tile_height > bottom_clip )
|
||
{
|
||
tile_height = bottom_clip - top;
|
||
}
|
||
src_y_offset = 0;
|
||
}
|
||
}
|
||
|
||
OSErr PreparePixmapForDrawing ( IL_Pixmap * image, IL_Pixmap * mask, Boolean canCopyMask,
|
||
DrawingState * state )
|
||
{
|
||
RGBColor rgb;
|
||
long index;
|
||
NI_IRGB * transparent_pixel;
|
||
IL_ColorSpace * color_space;
|
||
CTabHandle ctab;
|
||
|
||
state->copyMode = srcCopy;
|
||
state->pixmap = (NS_PixMap *) image->client_data;
|
||
state->mask = NULL;
|
||
|
||
if ( state->pixmap == NULL )
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
color_space = image->header.color_space;
|
||
XP_ASSERT(color_space);
|
||
|
||
if ( mask != NULL )
|
||
{
|
||
state->mask = (NS_PixMap *) mask->client_data;
|
||
}
|
||
|
||
/*
|
||
* If we can't copymask and we have a transparent colour, then we need to use QuickDraw's
|
||
* transparent mode and set the OpColor
|
||
*/
|
||
transparent_pixel = image->header.transparent_pixel;
|
||
|
||
if ( transparent_pixel != NULL )
|
||
{
|
||
/* Extract the transparent color for this pixmap */
|
||
rgb.red = (uint16) transparent_pixel->red | ( (uint16) transparent_pixel->red << 8 );
|
||
rgb.green = (uint16) transparent_pixel->green | ( (uint16) transparent_pixel->green << 8 );
|
||
rgb.blue = (uint16) transparent_pixel->blue | ( (uint16) transparent_pixel->blue << 8 );
|
||
|
||
/* if we have an indexed color space, then update the bg color. Grayscale color */
|
||
/* spaces assume the color's been mapped to the closest default gray */
|
||
if (color_space->type == NI_PseudoColor)
|
||
{
|
||
index = color_space->cmap.num_colors - 1;
|
||
|
||
ctab = state->pixmap->pixmap.pmTable;
|
||
(*ctab)->ctTable[ index ].rgb = rgb;
|
||
}
|
||
|
||
/*
|
||
* now, if we have a mask, but we can't copymask, then set our transfer mode
|
||
* to be transparent. We may have problems if the background color matches a
|
||
* color in the image, but that's life for now...
|
||
*/
|
||
if ( mask != NULL && !canCopyMask )
|
||
{
|
||
/* don't use the mask anymore */
|
||
state->mask = NULL;
|
||
|
||
state->copyMode = transparent;
|
||
RGBBackColor ( &rgb );
|
||
}
|
||
}
|
||
|
||
LockPixmapBuffer ( image );
|
||
if ( state->mask != NULL )
|
||
{
|
||
LockPixmapBuffer ( mask );
|
||
}
|
||
|
||
return noErr;
|
||
}
|
||
|
||
void DoneDrawingPixmap ( IL_Pixmap * image, IL_Pixmap * mask, DrawingState * state )
|
||
{
|
||
UnlockPixmapBuffer ( image );
|
||
if ( state->mask != NULL )
|
||
{
|
||
UnlockPixmapBuffer ( mask );
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* LockFEPixmapBuffer
|
||
*
|
||
* Locks the FE bits of the image
|
||
*/
|
||
void LockFEPixmapBuffer ( NS_PixMap* fe_pixmap )
|
||
{
|
||
if ( fe_pixmap != NULL )
|
||
{
|
||
if ( fe_pixmap->lock_count == 0 )
|
||
{
|
||
if ( fe_pixmap->buffer_handle != NULL )
|
||
{
|
||
HLock ( fe_pixmap->buffer_handle );
|
||
fe_pixmap->pixmap.baseAddr = *fe_pixmap->buffer_handle;
|
||
}
|
||
else
|
||
{
|
||
fe_pixmap->pixmap.baseAddr = (char *) fe_pixmap->image_buffer;
|
||
}
|
||
|
||
}
|
||
|
||
fe_pixmap->lock_count++;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* UnlockFEPixmapBuffer
|
||
*
|
||
* Unlocks the FE bits of the image
|
||
*/
|
||
void UnlockFEPixmapBuffer ( NS_PixMap* fe_pixmap )
|
||
{
|
||
if ( fe_pixmap != NULL )
|
||
{
|
||
if ( --fe_pixmap->lock_count == 0 )
|
||
{
|
||
if ( fe_pixmap->buffer_handle != NULL )
|
||
{
|
||
HUnlock ( fe_pixmap->buffer_handle );
|
||
}
|
||
|
||
fe_pixmap->pixmap.baseAddr = (Ptr) 0xFF5E0001;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* DestroyFEPixmap
|
||
*
|
||
* Tear down the NS_PixMap
|
||
*/
|
||
void DestroyFEPixmap ( NS_PixMap* fe_pixmap )
|
||
{
|
||
if ( !fe_pixmap )
|
||
return;
|
||
|
||
if ( fe_pixmap->buffer_handle != NULL )
|
||
DisposeHandle ( fe_pixmap->buffer_handle );
|
||
if ( fe_pixmap->image_buffer != NULL )
|
||
free ( fe_pixmap->image_buffer );
|
||
XP_DELETE ( fe_pixmap );
|
||
}
|
||
|
||
|
||
#pragma mark --- PRIVATE FUNCTIONS ---
|
||
|
||
|
||
/*
|
||
* Private Utilities
|
||
*/
|
||
|
||
static IL_GroupContext * CreateImageGroupContext ( MWContext * context )
|
||
{
|
||
IL_GroupContext * img_cx;
|
||
IMGCB * img_cb;
|
||
JMCException * exc;
|
||
|
||
exc = NULL;
|
||
|
||
img_cb = IMGCBFactory_Create( &exc );
|
||
if (exc)
|
||
{
|
||
/* XXXM12N Should really return exception */
|
||
JMC_DELETE_EXCEPTION( &exc );
|
||
return 0L;
|
||
}
|
||
|
||
/*
|
||
* Create an Image Group Context. IL_NewGroupContext augments the
|
||
* reference count for the JMC callback interface. The opaque
|
||
* argument to IL_NewGroupContext is the Front End's display
|
||
* context, which will be passed back to all the Image Library's
|
||
* FE callbacks.
|
||
*/
|
||
img_cx = IL_NewGroupContext( (void*) context, (IMGCBIF *)img_cb);
|
||
|
||
return img_cx;
|
||
}
|
||
|
||
static OSErr AllocatePixMap ( IL_Pixmap * il_pixmap, jint width, jint height,
|
||
NS_PixMap * fe_pixmap, CTabHandle ctab )
|
||
{
|
||
UInt32 rowbytes;
|
||
Uint32 pixmap_depth;
|
||
OSErr err;
|
||
IL_ColorSpace * color_space;
|
||
PixMap * pixmap;
|
||
Uint32 buffer_size;
|
||
|
||
err = noErr;
|
||
pixmap = &fe_pixmap->pixmap;
|
||
|
||
/*
|
||
* Sanity checks
|
||
*/
|
||
if ( il_pixmap == NULL || fe_pixmap == NULL || ctab == NULL )
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* Are we tiling this image?
|
||
*/
|
||
if ( il_pixmap->header.width == width && il_pixmap->header.height == height )
|
||
{
|
||
/* yup */
|
||
fe_pixmap->tiled = true;
|
||
}
|
||
else
|
||
{
|
||
/* nope, so use the dimensions of the src image as CopyBits can scale */
|
||
fe_pixmap->tiled = false;
|
||
width = il_pixmap->header.width;
|
||
height = il_pixmap->header.height;
|
||
}
|
||
|
||
/*
|
||
* Allocate the actual mac pixmap buffer
|
||
*/
|
||
|
||
color_space = il_pixmap->header.color_space;
|
||
pixmap_depth = color_space->pixmap_depth;
|
||
|
||
pixmap->bounds.top = 0;
|
||
pixmap->bounds.left = 0;
|
||
pixmap->bounds.right = width;
|
||
pixmap->bounds.bottom = height;
|
||
pixmap->pmVersion = 0;
|
||
pixmap->packType = 0;
|
||
pixmap->packSize = 0;
|
||
pixmap->hRes = 72L << 16;
|
||
pixmap->vRes = 72L << 16;
|
||
pixmap->pixelSize = pixmap_depth;
|
||
pixmap->planeBytes = 0;
|
||
pixmap->pmReserved = 0;
|
||
|
||
if ( pixmap_depth <= 8 )
|
||
{
|
||
/* set our pixel type to indexed */
|
||
pixmap->pixelType = 0;
|
||
pixmap->cmpCount = 1;
|
||
pixmap->cmpSize = pixmap_depth;
|
||
}
|
||
else
|
||
{
|
||
/* set our pixel type to direct color. we need to be sure to do this */
|
||
/* to prevent nasty crashes when saving to pictures/printing */
|
||
pixmap->pixelType = 0x10;
|
||
pixmap->cmpCount = 3;
|
||
pixmap->cmpSize = pixmap_depth == 16 ? 5 : 8;
|
||
}
|
||
|
||
pixmap->pmTable = ctab;
|
||
|
||
rowbytes = width * pixmap_depth;
|
||
rowbytes = (( rowbytes + 31 ) >> 5) << 2;
|
||
|
||
/*
|
||
* Sanity check rowBytes to make sure it's not over the QuickDraw limit
|
||
* This will cause the calling code to downgrade the bit depth until we can allocate
|
||
* the image, or just fail if it's too big overall
|
||
*/
|
||
if ( rowbytes > 0x3FFF )
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
il_pixmap->header.widthBytes = rowbytes;
|
||
|
||
pixmap->rowBytes = rowbytes | 0x8000;
|
||
|
||
/*
|
||
* Allocate the buffer. If the image is really large, we just allocate out of temp
|
||
* memory directly as it won't force the allocator to allocate a big tem mem chunk
|
||
* which might be filled with other blocks that will force it to hang around for a
|
||
* long time.
|
||
*
|
||
* I will probably change the large block allocator to always use temp memory for
|
||
* huge blocks as it will simplify this code and fragment the large block heap less.
|
||
*/
|
||
buffer_size = rowbytes * height;
|
||
if ( buffer_size > 63 * 1024L )
|
||
{
|
||
fe_pixmap->buffer_handle = TempNewHandle ( buffer_size, &err );
|
||
|
||
/*
|
||
* If this fails, then go ahead and try malloc...
|
||
*/
|
||
if ( fe_pixmap->buffer_handle == NULL )
|
||
{
|
||
fe_pixmap->image_buffer = malloc ( buffer_size );
|
||
if ( fe_pixmap->image_buffer == NULL )
|
||
{
|
||
err = memFullErr;
|
||
}
|
||
else
|
||
{
|
||
err = noErr;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fe_pixmap->image_buffer = malloc ( buffer_size );
|
||
if ( fe_pixmap->image_buffer == NULL )
|
||
{
|
||
err = memFullErr;
|
||
}
|
||
}
|
||
|
||
pixmap->baseAddr = (Ptr) 0xFF5E0001;
|
||
il_pixmap->bits = (Ptr) 0xFF5E0001;
|
||
|
||
#if TRACK_IMAGE_CACHE_SIZE
|
||
if ( err == noErr )
|
||
{
|
||
gCacheSize += buffer_size;
|
||
if ( gCacheSize > gMaxCacheSize )
|
||
gMaxCacheSize = gCacheSize;
|
||
}
|
||
#endif
|
||
|
||
return err;
|
||
}
|
||
|
||
static ColorSpaceIndex GetNextBestColorSpaceIndex ( ColorSpaceIndex index )
|
||
{
|
||
switch ( index )
|
||
{
|
||
default: index = kNumColorSpaces; break;
|
||
case kOneBit: index = kNumColorSpaces; break;
|
||
case kEightBitColor: index = kOneBit; break;
|
||
case kSixteenBit: index = kEightBitColor; break;
|
||
case kThirtytwoBit: index = kSixteenBit; break;
|
||
}
|
||
|
||
return index;
|
||
}
|
||
|
||
static ColorSpaceIndex ConvertColorSpaceToIndex ( IL_ColorSpace * color_space )
|
||
{
|
||
ColorSpaceIndex index;
|
||
|
||
index = kEightBitColor;
|
||
|
||
switch ( color_space->type )
|
||
{
|
||
case NI_TrueColor:
|
||
if ( color_space->pixmap_depth > 16 )
|
||
{
|
||
index = kThirtytwoBit;
|
||
}
|
||
else
|
||
{
|
||
index = kSixteenBit;
|
||
}
|
||
|
||
break;
|
||
|
||
case NI_PseudoColor:
|
||
switch ( color_space->pixmap_depth )
|
||
{
|
||
case 1: index = kOneBit; break;
|
||
case 2: index = kEightBitColor; break;
|
||
case 4: index = kEightBitColor; break;
|
||
case 8: index = kEightBitColor; break;
|
||
}
|
||
|
||
break;
|
||
|
||
case NI_GreyScale:
|
||
switch ( color_space->pixmap_depth )
|
||
{
|
||
case 1: index = kOneBit; break;
|
||
case 2: index = kEightBitGray; break;
|
||
case 4: index = kEightBitGray; break;
|
||
case 8: index = kEightBitGray; break;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
return index;
|
||
}
|
||
|
||
static ColorSpaceIndex ConvertDepthToColorSpaceIndex ( Int32 depth, Boolean grayscale )
|
||
{
|
||
ColorSpaceIndex index;
|
||
GDHandle gd;
|
||
|
||
/*
|
||
* We default image depth to the depth of the deepest screen.
|
||
*/
|
||
if ( depth == 0 )
|
||
{
|
||
gd = GetDeepestDevice();
|
||
depth = GetDepth ( gd );
|
||
|
||
/* check that magic bit to see if it's a grayscale device */
|
||
grayscale = ( (*gd)->gdFlags & ( 1 << gdDevType ) ) == 0;
|
||
}
|
||
|
||
/*
|
||
* If we're looking at allocating a direct pixmap, and the user's machine
|
||
* is low on temp memory, then downgrade to 8 bit
|
||
*/
|
||
if ( ( depth >= 16 ) && ( TempFreeMem() < ( 2048L * 1024L )) )
|
||
{
|
||
depth = 8;
|
||
}
|
||
|
||
if ( grayscale )
|
||
{
|
||
switch ( depth )
|
||
{
|
||
case 1: index = kOneBit; break;
|
||
case 2: index = kEightBitGray; break;
|
||
case 4: index = kEightBitGray; break;
|
||
case 8: index = kEightBitGray; break;
|
||
case 16: index = kSixteenBit; break;
|
||
case 32: index = kThirtytwoBit; break;
|
||
default: index = kEightBitColor; break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
switch ( depth )
|
||
{
|
||
case 1: index = kOneBit; break;
|
||
case 2: index = kEightBitColor; break;
|
||
case 4: index = kEightBitColor; break;
|
||
case 8: index = kEightBitColor; break;
|
||
case 16: index = kSixteenBit; break;
|
||
case 32: index = kThirtytwoBit; break;
|
||
default: index = kEightBitColor; break;
|
||
}
|
||
}
|
||
|
||
return index;
|
||
}
|
||
|
||
static IL_ColorSpace * AllocateColorSpace ( MWContext * context, ColorSpaceIndex space_index )
|
||
{
|
||
IL_RGBBits rgb;
|
||
Int32 depth;
|
||
Boolean grayscale;
|
||
IL_ColorMap * color_map;
|
||
IL_ColorSpace * color_space;
|
||
Int32 index;
|
||
Uint8 * index_map;
|
||
Int32 num_colors;
|
||
IL_IRGB transparent_color;
|
||
|
||
color_space = NULL;
|
||
|
||
/* convert the space index back to depth/grayscale */
|
||
switch ( space_index )
|
||
{
|
||
default: return NULL;
|
||
case kOneBit: depth = 1; grayscale = true; break;
|
||
case kEightBitGray: depth = 8; grayscale = true; break;
|
||
case kEightBitColor: depth = 8; grayscale = false; break;
|
||
case kSixteenBit: depth = 16; grayscale = false; break;
|
||
case kThirtytwoBit: depth = 32; grayscale = false; break;
|
||
}
|
||
|
||
if ( depth == 16 )
|
||
{
|
||
/*
|
||
* Create a 16 bit color space
|
||
*/
|
||
rgb.red_bits = 5;
|
||
rgb.red_shift = 10;
|
||
rgb.green_bits = 5;
|
||
rgb.green_shift = 5;
|
||
rgb.blue_bits = 5;
|
||
rgb.blue_shift = 0;
|
||
|
||
color_space = IL_CreateTrueColorSpace ( &rgb, 16 );
|
||
}
|
||
else
|
||
if ( depth == 32 )
|
||
{
|
||
/*
|
||
* Create a 32 bit color space
|
||
*/
|
||
rgb.red_bits = 8;
|
||
rgb.red_shift = 16;
|
||
rgb.green_bits = 8;
|
||
rgb.green_shift = 8;
|
||
rgb.blue_bits = 8;
|
||
rgb.blue_shift = 0;
|
||
|
||
color_space = IL_CreateTrueColorSpace ( &rgb, 32 );
|
||
}
|
||
else
|
||
if ( grayscale )
|
||
{
|
||
/*
|
||
* Create an indexed grayscale space.
|
||
*/
|
||
color_space = IL_CreateGreyScaleColorSpace ( depth, depth );
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* Create an indexed color space.
|
||
*/
|
||
|
||
/*
|
||
* First create a color map with a reserved color for the transparent color.
|
||
* When we first create a context, we won't yet have a transparent color, so
|
||
* then we just set it to the default background color
|
||
*/
|
||
if ( context->transparent_pixel != NULL )
|
||
{
|
||
transparent_color.red = context->transparent_pixel->red;
|
||
transparent_color.green = context->transparent_pixel->green;
|
||
transparent_color.blue = context->transparent_pixel->blue;
|
||
}
|
||
else
|
||
{
|
||
transparent_color.red = kDefaultBGColorRed;
|
||
transparent_color.green = kDefaultBGColorGreen;
|
||
transparent_color.blue = kDefaultBGColorBlue;
|
||
}
|
||
|
||
transparent_color.index = 0;
|
||
|
||
color_map = IL_NewCubeColorMap ( NULL, 0, 1 << depth );
|
||
|
||
/* Allocate the index map for this color map */
|
||
if ( color_map != NULL )
|
||
{
|
||
IL_AddColorToColorMap ( color_map, &transparent_color );
|
||
|
||
num_colors = ( 1 << depth );
|
||
|
||
index_map = (Uint8 *) XP_ALLOC ( sizeof(uint8) * num_colors );
|
||
if ( index_map != NULL )
|
||
{
|
||
for ( index = num_colors - 1; index >= 0; --index )
|
||
{
|
||
index_map[ index ] = index;
|
||
}
|
||
|
||
color_map->index = index_map;
|
||
}
|
||
else
|
||
{
|
||
IL_DestroyColorMap ( color_map );
|
||
color_map = NULL;
|
||
}
|
||
}
|
||
|
||
/* Now build a color space around it */
|
||
if ( color_map != NULL )
|
||
{
|
||
color_space = IL_CreatePseudoColorSpace ( color_map, depth, depth );
|
||
if ( color_space == NULL )
|
||
{
|
||
IL_DestroyColorMap ( color_map );
|
||
color_map = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
return color_space;
|
||
}
|
||
|
||
static IL_ColorSpace * GetColorSpace ( MWContext * context, ColorSpaceIndex space_index, CTabHandle * color_table )
|
||
{
|
||
IL_ColorSpace * color_space;
|
||
CTabHandle ctable;
|
||
|
||
/* bounds check */
|
||
if ( space_index >= kNumColorSpaces )
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
ctable = NULL;
|
||
if ( color_table != NULL )
|
||
{
|
||
*color_table = NULL;
|
||
}
|
||
|
||
color_space = gColorSpaces[ space_index ];
|
||
|
||
/*
|
||
* Do we need to allocate a space?
|
||
*/
|
||
if ( color_space == NULL )
|
||
{
|
||
color_space = AllocateColorSpace ( context, space_index );
|
||
|
||
/*
|
||
* Allocate a mac color table for this color space
|
||
*/
|
||
if ( color_space != NULL )
|
||
{
|
||
ctable = ConvertColorSpaceToColorTable ( color_space );
|
||
if ( ctable == NULL )
|
||
{
|
||
IL_ReleaseColorSpace ( color_space );
|
||
color_space = NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Add a reference to this color space so we're sure no one ever deletes it
|
||
* out from under us (we keep them cached for the life of the browser).
|
||
*/
|
||
IL_AddRefToColorSpace ( color_space );
|
||
|
||
gColorSpaces[ space_index ] = color_space;
|
||
gColorTables [ space_index ] = ctable;
|
||
}
|
||
|
||
if ( color_space != NULL )
|
||
{
|
||
ctable = gColorTables [ space_index ];
|
||
}
|
||
|
||
if ( color_table != NULL )
|
||
{
|
||
*color_table = ctable;
|
||
}
|
||
|
||
return color_space;
|
||
}
|
||
|
||
static CTabHandle ConvertColorSpaceToColorTable ( IL_ColorSpace * color_space )
|
||
{
|
||
CTabHandle ctab;
|
||
ColorSpec * cspecs;
|
||
uint32 ctab_entries;
|
||
uint32 count;
|
||
NI_RGB * map_colors;
|
||
|
||
ctab = NULL;
|
||
if ( color_space->pixmap_depth <= 8 )
|
||
{
|
||
ctab_entries = color_space->cmap.num_colors;
|
||
}
|
||
else
|
||
{
|
||
ctab_entries = 1;
|
||
}
|
||
|
||
ctab = (CTabHandle) NewHandleClear ( sizeof(ColorTable) +
|
||
sizeof(ColorSpec) * (ctab_entries - 1) );
|
||
if ( ctab != NULL )
|
||
{
|
||
(*ctab)->ctSeed = GetCTSeed();
|
||
(*ctab)->ctSize = ctab_entries - 1;
|
||
|
||
/*
|
||
* Grab the color from the color map. If we're direct, then don't bother.
|
||
*/
|
||
if ( color_space->pixmap_depth <= 8 )
|
||
{
|
||
cspecs = &(*ctab)->ctTable[ 0 ];
|
||
|
||
if ( color_space->type == NI_GreyScale )
|
||
{
|
||
Uint16 color;
|
||
Uint16 color_inc;
|
||
|
||
color_inc = 256 / ( 1 << color_space->pixmap_depth );
|
||
color_inc |= color_inc << 8;
|
||
|
||
color = 0;
|
||
for ( count = 0; count < ctab_entries; ++count )
|
||
{
|
||
cspecs->value = count;
|
||
cspecs->rgb.red = color;
|
||
cspecs->rgb.green = color;
|
||
cspecs->rgb.blue = color;
|
||
|
||
cspecs++;
|
||
color += color_inc;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
map_colors = &color_space->cmap.map[ 0 ];
|
||
|
||
for ( count = 0; count < ctab_entries; ++count )
|
||
{
|
||
cspecs->value = count;
|
||
cspecs->rgb.red = ((uint16) map_colors->red << 8 ) | (uint16) map_colors->red;
|
||
cspecs->rgb.green = ((uint16) map_colors->green << 8 ) | (uint16) map_colors->green;
|
||
cspecs->rgb.blue = ((uint16) map_colors->blue << 8 ) | (uint16) map_colors->blue;
|
||
|
||
cspecs++;
|
||
map_colors++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return ctab;
|
||
}
|
||
|
||
static void SetColorSpaceTransparentColor ( IL_ColorSpace * color_space, Uint8 red, Uint8 green, Uint8 blue )
|
||
{
|
||
if ( color_space->type == NI_PseudoColor )
|
||
{
|
||
IL_RGB *map;
|
||
|
||
/*
|
||
* The last color in the color map is always our transparent color
|
||
*/
|
||
map = &color_space->cmap.map[ color_space->cmap.num_colors - 1 ];
|
||
map->red = red;
|
||
map->green = green;
|
||
map->blue = blue;
|
||
}
|
||
}
|
||
|
||
static OSErr CreatePictureGWorld ( IL_Pixmap * image, IL_Pixmap * mask, PictureGWorldState * state )
|
||
{
|
||
OSErr err;
|
||
CTabHandle ctab;
|
||
NI_IRGB * transparent_pixel;
|
||
IL_ColorSpace * color_space;
|
||
CGrafPtr savePort;
|
||
GDHandle saveGD;
|
||
Boolean remapTransparentIndex;
|
||
|
||
remapTransparentIndex = false;
|
||
|
||
state->image = (NS_PixMap *) image->client_data;
|
||
|
||
if ( mask != NULL )
|
||
{
|
||
state->mask = (NS_PixMap *) mask->client_data;
|
||
|
||
color_space = image->header.color_space;
|
||
XP_ASSERT(color_space);
|
||
|
||
transparent_pixel = image->header.transparent_pixel;
|
||
XP_ASSERT(transparent_pixel);
|
||
|
||
/* get our own expended copy of the background/transparent color */
|
||
state->transparentColor.red = (uint16) transparent_pixel->red | ( (uint16) transparent_pixel->red << 8 );
|
||
state->transparentColor.green = (uint16) transparent_pixel->green | ( (uint16) transparent_pixel->green << 8 );
|
||
state->transparentColor.blue = (uint16) transparent_pixel->blue | ( (uint16) transparent_pixel->blue << 8 );
|
||
|
||
/*
|
||
* If the image has an indexed color color_space, then we know it has a unique
|
||
* transparent color. So, we can use it's color table for the copybits
|
||
*/
|
||
if ( image->header.color_space->type == NI_PseudoColor )
|
||
{
|
||
ctab = state->image->pixmap.pmTable;
|
||
err = HandToHand ( (Handle *) &ctab );
|
||
if ( err != noErr )
|
||
return err;
|
||
|
||
/* make sure the background entry contains the correct color */
|
||
state->transparentIndex = color_space->cmap.num_colors - 1;
|
||
(*ctab)->ctTable[ state->transparentIndex ].rgb = state->transparentColor;
|
||
remapTransparentIndex = true;
|
||
}
|
||
else
|
||
/*
|
||
* If the image has a grayscale space, we're in a world of hurt. We need to steal
|
||
* an index to use for the transparent color
|
||
*/
|
||
if ( image->header.color_space->type == NI_GreyScale )
|
||
{
|
||
ctab = state->image->pixmap.pmTable;
|
||
err = HandToHand ( (Handle *) &ctab );
|
||
if ( err != noErr )
|
||
return err;
|
||
|
||
/* use the default transparent color, but find it's true index */
|
||
state->transparentIndex = -1;
|
||
remapTransparentIndex = true;
|
||
}
|
||
else
|
||
/*
|
||
* The image has a true color space. Hopefully, the transparent color is unique to the
|
||
* image. If not, (ie the site's bg color is the same as a valid color in the image), we'll
|
||
* have more be transparent than should be. There's not a whole lot we can do about this
|
||
*/
|
||
{
|
||
ctab = NULL;
|
||
}
|
||
|
||
state->ctab = ctab;
|
||
|
||
/*
|
||
* Make sure that our transparent color is unique. We can choose any color we want as it
|
||
* won't actually be displayed.
|
||
*/
|
||
CreateUniqueTransparentColor ( image, state );
|
||
|
||
err = NewGWorld ( &state->gworld, image->header.color_space->pixmap_depth,
|
||
&state->image->pixmap.bounds, ctab, NULL, useTempMem );
|
||
|
||
/*
|
||
* If we have an indexed image, then find out where the transparent color maps to
|
||
* so that we can remove it
|
||
*/
|
||
if ( err == noErr && remapTransparentIndex )
|
||
{
|
||
GetGWorld ( &savePort, &saveGD );
|
||
SetGWorld ( state->gworld, NULL );
|
||
|
||
/* where does our transparent color really map to? */
|
||
state->transparentIndex = Color2Index ( &state->transparentColor );
|
||
|
||
/* Remove this from the inverse table */
|
||
ReserveEntry ( state->transparentIndex, true );
|
||
|
||
/*
|
||
* get the real RGB color this maps to (should always be the same for PseudoColor but
|
||
* could be different for grayscape where we may not have a unique color).
|
||
*/
|
||
Index2Color ( state->transparentIndex, &state->transparentColor );
|
||
|
||
SetGWorld ( savePort, saveGD );
|
||
}
|
||
else
|
||
{
|
||
state->transparentIndex = -1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
state->mask = NULL;
|
||
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
static void TeardownPictureGWorld ( PictureGWorldState * state )
|
||
{
|
||
if ( state->gworld != NULL )
|
||
{
|
||
DisposeGWorld ( state->gworld );
|
||
}
|
||
|
||
if ( state->ctab != NULL )
|
||
{
|
||
DisposeCTable ( state->ctab );
|
||
}
|
||
}
|
||
|
||
static void CreateUniqueTransparentColor ( IL_Pixmap * image, PictureGWorldState * state )
|
||
{
|
||
RGBColor rgb;
|
||
Boolean foundColor;
|
||
|
||
if ( state->ctab == NULL )
|
||
return;
|
||
|
||
/*
|
||
* We have no idea which colors are actually used in the image (unless we want to actually
|
||
* go over each pixel).
|
||
*
|
||
* For direct pixels and grayscale images, we just use the transparent color that came along
|
||
* with the page. On color images, we actually try to find a unique color
|
||
*/
|
||
|
||
#define INV_TAB_RES 4
|
||
#define INV_TAB_MAX ((Uint16)~((0x8000 >> (INV_TAB_RES-1)) - 1))
|
||
#define INV_TAB_MASK(c) ((c) & INV_TAB_MAX)
|
||
#define INV_TAB_INC (0x8000 >> (INV_TAB_RES-1))
|
||
|
||
if ( image->header.color_space->type == NI_PseudoColor )
|
||
{
|
||
/*
|
||
* We assume QD will use it's default 4bit inverse table, so try and find a unique color
|
||
* within that space.
|
||
* First look for our default transparent color. Most of the time it should be unique.
|
||
*/
|
||
|
||
if ( FindColorInCTable ( state->ctab, state->transparentIndex, &state->transparentColor ))
|
||
{
|
||
foundColor = false;
|
||
|
||
/* we found that color, so run through all the colors in the hope of finding something unique */
|
||
for ( rgb.red = 0; (Uint16)rgb.red <= INV_TAB_MAX; rgb.red += INV_TAB_INC )
|
||
{
|
||
for ( rgb.green = 0; (Uint16)rgb.green <= INV_TAB_MAX; rgb.green += INV_TAB_INC )
|
||
{
|
||
for ( rgb.blue = 0; (Uint16)rgb.blue <= INV_TAB_MAX; rgb.blue += INV_TAB_INC )
|
||
{
|
||
if ( FindColorInCTable ( state->ctab, state->transparentIndex, &rgb ) == false )
|
||
{
|
||
foundColor = true;
|
||
goto foundit;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
foundit:
|
||
/* if we found a color, then use it */
|
||
if ( foundColor )
|
||
{
|
||
state->transparentColor = rgb;
|
||
(*state->ctab)->ctTable[ state->transparentIndex ].rgb = rgb;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static Boolean FindColorInCTable ( CTabHandle ctab, Uint32 skipIndex, RGBColor * rgb )
|
||
{
|
||
Boolean colorIsUsed;
|
||
Uint32 count;
|
||
UInt32 num_entries;
|
||
ColorSpec * cspecs;
|
||
|
||
cspecs = (*ctab)->ctTable;
|
||
num_entries = (*ctab)->ctSize;
|
||
colorIsUsed = false;
|
||
|
||
/* run through the color table and try to find this color */
|
||
for ( count = 0; count < num_entries; ++count )
|
||
{
|
||
/* don't bother checking the current transparent color */
|
||
if ( count != skipIndex )
|
||
{
|
||
/* make sure the color is unique within the resolution of the inverse table */
|
||
if ( INV_TAB_MASK(cspecs[count].rgb.red) == INV_TAB_MASK(rgb->red) &&
|
||
INV_TAB_MASK(cspecs[count].rgb.green) == INV_TAB_MASK(rgb->green) &&
|
||
INV_TAB_MASK(cspecs[count].rgb.blue) == INV_TAB_MASK(rgb->blue) )
|
||
{
|
||
colorIsUsed = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return colorIsUsed;
|
||
}
|
||
|
||
static void CopyPicture ( PictureGWorldState * state )
|
||
{
|
||
CGrafPtr savePort;
|
||
GDHandle saveGD;
|
||
PixMapHandle pm;
|
||
SInt8 hState;
|
||
|
||
GetGWorld ( &savePort, &saveGD );
|
||
pm = GetGWorldPixMap ( state->gworld );
|
||
hState = HGetState ( (Handle) pm );
|
||
|
||
LockPixels ( pm );
|
||
|
||
SetGWorld ( state->gworld, NULL );
|
||
|
||
/* copy our image into the gworld */
|
||
CopyBits ( (BitMap *) &state->image->pixmap, (BitMap *) *pm, &state->image->pixmap.bounds,
|
||
&state->image->pixmap.bounds, srcCopy, NULL );
|
||
|
||
/* make our transparent index writeable again */
|
||
if ( state->transparentIndex != -1 )
|
||
{
|
||
ReserveEntry ( state->transparentIndex, false );
|
||
|
||
/* make sure the value field in the color table for the transparent entry is correct */
|
||
(*(*pm)->pmTable)->ctTable[ state->transparentIndex ].value = state->transparentIndex;
|
||
}
|
||
|
||
/* fill the masked area with this color */
|
||
RGBForeColor ( &state->transparentColor );
|
||
|
||
CopyBits ( (BitMap *) &state->mask->pixmap, (BitMap *) *pm, &state->mask->pixmap.bounds,
|
||
&state->image->pixmap.bounds, notSrcOr, NULL );
|
||
|
||
SetGWorld ( savePort, saveGD );
|
||
|
||
/* now actually copy the picture data, making the transparent color transparent */
|
||
ForeColor ( blackColor );
|
||
RGBBackColor ( &state->transparentColor );
|
||
|
||
CopyBits ( (BitMap *) *pm, &qd.thePort->portBits, &(*pm)->bounds, &(*pm)->bounds, transparent, NULL );
|
||
|
||
UnlockPixels ( pm );
|
||
HSetState ( (Handle) pm, hState );
|
||
}
|
||
|
||
|
||
static void LockPixmapBuffer ( IL_Pixmap * pixmap )
|
||
{
|
||
NS_PixMap * fe_pixmap;
|
||
|
||
fe_pixmap = (NS_PixMap *) pixmap->client_data;
|
||
LockFEPixmapBuffer ( fe_pixmap );
|
||
pixmap->bits = fe_pixmap->pixmap.baseAddr; // bits now point to something normal
|
||
}
|
||
|
||
|
||
static void UnlockPixmapBuffer ( IL_Pixmap * pixmap )
|
||
{
|
||
NS_PixMap * fe_pixmap;
|
||
|
||
fe_pixmap = (NS_PixMap *) pixmap->client_data;
|
||
UnlockFEPixmapBuffer ( fe_pixmap );
|
||
pixmap->bits = fe_pixmap->pixmap.baseAddr; // bits set to garbage
|
||
}
|
||
|
||
|
||
|
||
static CIconHandle GetIconHandle ( jint iconID )
|
||
{
|
||
CIconHandle ic;
|
||
|
||
if ( iconID == IL_IMAGE_EMBED )
|
||
{
|
||
iconID = IL_IMAGE_BAD_DATA;
|
||
}
|
||
|
||
iconID += IL_ICON_OFFSET;
|
||
|
||
ic = CIconList::GetIcon( iconID );
|
||
if ( !ic )
|
||
{
|
||
if (iconID >= IL_GOPHER_FIRST)
|
||
iconID = IL_GOPHER_FIRST-1;
|
||
else if (iconID >= IL_NEWS_FIRST)
|
||
iconID = IL_NEWS_FIRST-1;
|
||
else
|
||
iconID = IL_IMAGE_FIRST-1;
|
||
iconID = iconID + IL_ICON_OFFSET;
|
||
ic = CIconList::GetIcon(iconID);
|
||
}
|
||
|
||
return ic;
|
||
}
|
||
|
||
void
|
||
ImageGroupObserver(XP_Observable /*observable*/,
|
||
XP_ObservableMsg message,
|
||
void* /*message_data*/,
|
||
void* closure)
|
||
{
|
||
MWContext* theContext = static_cast<MWContext*>(closure);
|
||
Assert_(theContext);
|
||
if (!theContext)
|
||
return;
|
||
|
||
CBrowserContext* theBrowserContext = dynamic_cast<CBrowserContext*>(theContext->fe.newContext);
|
||
Assert_(theContext);
|
||
if (!theBrowserContext)
|
||
return;
|
||
|
||
switch(message)
|
||
{
|
||
case IL_STARTED_LOADING:
|
||
theBrowserContext->SetImagesLoading(true);
|
||
break;
|
||
|
||
case IL_ABORTED_LOADING:
|
||
theBrowserContext->SetImagesDelayed(true);
|
||
break;
|
||
|
||
case IL_FINISHED_LOADING:
|
||
theBrowserContext->SetImagesLoading(false);
|
||
break;
|
||
|
||
case IL_STARTED_LOOPING:
|
||
theBrowserContext->SetImagesLooping(true);
|
||
break;
|
||
|
||
case IL_FINISHED_LOOPING:
|
||
theBrowserContext->SetImagesLooping(false);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void
|
||
FE_MochaImageGroupObserver(XP_Observable /*observable*/,
|
||
XP_ObservableMsg message,
|
||
void *message_data,
|
||
void */*closure*/)
|
||
{
|
||
IL_GroupMessageData *data = (IL_GroupMessageData *)message_data;
|
||
MWContext *theContext = (MWContext *)data->display_context;
|
||
|
||
// If we are passed a NULL display context, the MWContext has been
|
||
// destroyed.
|
||
if (!theContext)
|
||
return;
|
||
|
||
CBrowserContext* theBrowserContext = dynamic_cast<CBrowserContext*>(theContext->fe.newContext);
|
||
if (!theBrowserContext)
|
||
return;
|
||
|
||
switch(message)
|
||
{
|
||
case IL_STARTED_LOADING:
|
||
theBrowserContext->SetMochaImagesLoading(true);
|
||
break;
|
||
|
||
case IL_ABORTED_LOADING:
|
||
theBrowserContext->SetMochaImagesDelayed(true);
|
||
break;
|
||
|
||
case IL_FINISHED_LOADING:
|
||
theBrowserContext->SetMochaImagesLoading(false);
|
||
break;
|
||
|
||
case IL_STARTED_LOOPING:
|
||
theBrowserContext->SetMochaImagesLooping(true);
|
||
break;
|
||
|
||
case IL_FINISHED_LOOPING:
|
||
theBrowserContext->SetMochaImagesLooping(false);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
#ifdef PROFILE
|
||
#pragma profile off
|
||
#endif
|
||
|