mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-13 01:55:44 +00:00
506 lines
11 KiB
C++
506 lines
11 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.
|
|
*/
|
|
|
|
// drawable.cpp - Classes used for maintaining the notion of
|
|
// offscreen and onscreen drawables, so that
|
|
// document contents can be drawn to either
|
|
// location. Currently used for LAYERS.
|
|
|
|
//
|
|
// To Do:
|
|
// 1. Allocate a GWorld that matches the depth of the screen the window is
|
|
// currently being displayed on. This will involve cooperation from
|
|
// CHTMLView.cp and could well be done through the Lock mechanism assuming
|
|
// there's a way to get back at where the fe window is.
|
|
//
|
|
// 2. Keep the GWorld around and mark the pixels purgeable. LockDrawable would
|
|
// then call UpdateGWorld before it locked the pixels.
|
|
//
|
|
// 3. Take out the parent stuff. It isn't useful anymore unless it ends up
|
|
// being needed for item 1 above (which I don't think it will).
|
|
//
|
|
// 4. Allocate a COffscreenDrawable per CHTMLView but then share the actual
|
|
// GWorld between them all. That would allow the CHTMLView to update the
|
|
// drawable whenever the scrolling origin changes. COffscreenDrawable can
|
|
// then offset the clip when it gets set as opposed to doing it both in
|
|
// SetLayerClip and FocusDraw (and CHTMLView wouldn't need to override
|
|
// SetLayerClip anymore).
|
|
//
|
|
|
|
#ifdef LAYERS
|
|
|
|
#include "CDrawable.h"
|
|
|
|
COffscreenDrawable *COffscreenDrawable::mOffscreenDrawable = NULL;
|
|
|
|
// CDrawable base class
|
|
CDrawable::CDrawable()
|
|
{
|
|
mParent = NULL;
|
|
mGWorld = NULL;
|
|
mRefCnt = 0;
|
|
mOriginX = mOriginY = 0;
|
|
mClipRgn = NewRgn();
|
|
mClipChanged = false;
|
|
}
|
|
|
|
// CDrawable base class
|
|
CDrawable::~CDrawable()
|
|
{
|
|
DisposeRgn ( mClipRgn );
|
|
}
|
|
|
|
// Each user should call InitDrawable before using the drawable
|
|
// and RelinquishDrawable when it is done. This allows us to
|
|
// maintain a reference count of users.
|
|
void CDrawable::InitDrawable(
|
|
CL_Drawable* /* inCLDrawable */)
|
|
{
|
|
mRefCnt++;
|
|
}
|
|
|
|
void CDrawable::RelinquishDrawable(
|
|
CL_Drawable* /* inCLDrawable */)
|
|
{
|
|
Assert_(mRefCnt > 0);
|
|
|
|
if (mRefCnt)
|
|
{
|
|
mRefCnt--;
|
|
}
|
|
}
|
|
|
|
// Set and get the origin of the drawable
|
|
void CDrawable::SetLayerOrigin(
|
|
int32 inX,
|
|
int32 inY)
|
|
{
|
|
mOriginX = inX;
|
|
mOriginY = inY;
|
|
}
|
|
|
|
void CDrawable::GetLayerOrigin(
|
|
int32* outX,
|
|
int32* outY)
|
|
{
|
|
*outX = mOriginX;
|
|
*outY = mOriginY;
|
|
}
|
|
|
|
// Set the clip region of the drawable.
|
|
void CDrawable::SetLayerClip(
|
|
FE_Region inClipRgn)
|
|
{
|
|
|
|
if ( inClipRgn != NULL )
|
|
{
|
|
CopyRgn ( FE_GetMDRegion(inClipRgn), mClipRgn );
|
|
mClipChanged = true;
|
|
|
|
// if we're the current port, make the change be immediate. I wish layers
|
|
// would tell the drawable directly that it's being made active rather than
|
|
// calling a global FE entry point
|
|
if ( UQDGlobals::GetCurrentPort() == (GrafPtr) mGWorld )
|
|
{
|
|
mClipChanged = false;
|
|
|
|
::OffsetRgn ( mClipRgn, -mGWorld->portRect.left, -mGWorld->portRect.top );
|
|
|
|
::SetClip ( mClipRgn );
|
|
|
|
::OffsetRgn ( mClipRgn, mGWorld->portRect.left, mGWorld->portRect.top );
|
|
}
|
|
}
|
|
}
|
|
|
|
Boolean CDrawable::HasClipChanged()
|
|
{
|
|
Boolean changed;
|
|
|
|
changed = mClipChanged;
|
|
mClipChanged = false;
|
|
|
|
return changed;
|
|
}
|
|
|
|
void CDrawable::CopyPixels(
|
|
CDrawable* /* inSrcDrawable */,
|
|
FE_Region /* inCopyRgn */)
|
|
{
|
|
}
|
|
|
|
void CDrawable::SetParent(
|
|
CDrawable* parent )
|
|
{
|
|
mParent = parent;
|
|
}
|
|
|
|
|
|
COnscreenDrawable::COnscreenDrawable()
|
|
{
|
|
}
|
|
|
|
|
|
CRouterDrawable::CRouterDrawable()
|
|
{
|
|
}
|
|
|
|
void CRouterDrawable::SetLayerOrigin(
|
|
int32 inX,
|
|
int32 inY)
|
|
{
|
|
if ( mParent != NULL )
|
|
{
|
|
mParent->SetLayerOrigin ( inX, inY );
|
|
}
|
|
}
|
|
|
|
void CRouterDrawable::GetLayerOrigin(
|
|
int32* outX,
|
|
int32* outY)
|
|
{
|
|
if ( mParent != NULL )
|
|
{
|
|
mParent->GetLayerOrigin ( outX, outY );
|
|
}
|
|
}
|
|
|
|
void CRouterDrawable::SetLayerClip(
|
|
FE_Region inClipRgn)
|
|
{
|
|
if ( mParent != NULL )
|
|
{
|
|
mParent->SetLayerClip ( inClipRgn );
|
|
}
|
|
}
|
|
|
|
FE_Region CRouterDrawable::GetLayerClip()
|
|
{
|
|
FE_Region clip;
|
|
|
|
clip = mClipRgn;
|
|
if ( mParent != NULL )
|
|
{
|
|
clip = mParent->GetLayerClip();
|
|
}
|
|
|
|
return clip;
|
|
}
|
|
|
|
Boolean CRouterDrawable::HasClipChanged()
|
|
{
|
|
Boolean changed;
|
|
|
|
changed = mClipChanged;
|
|
if ( mParent != NULL )
|
|
{
|
|
changed = mParent->HasClipChanged();
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
void CRouterDrawable::CopyPixels(
|
|
CDrawable * inSrcDrawable,
|
|
FE_Region hCopyRgn)
|
|
{
|
|
if ( mParent != NULL )
|
|
{
|
|
mParent->CopyPixels ( inSrcDrawable, hCopyRgn );
|
|
}
|
|
}
|
|
|
|
// For the current implementation, only a single offscreen drawable
|
|
// is created using this factory method.
|
|
COffscreenDrawable * COffscreenDrawable::AllocateOffscreen()
|
|
{
|
|
if (mOffscreenDrawable == NULL)
|
|
{
|
|
mOffscreenDrawable = new COffscreenDrawable();
|
|
}
|
|
|
|
return mOffscreenDrawable;
|
|
}
|
|
|
|
COffscreenDrawable::COffscreenDrawable()
|
|
{
|
|
mGWorld = NULL;
|
|
mOwner = NULL;
|
|
mWidth = mHeight = 0;
|
|
}
|
|
|
|
// Get rid of any offscreen constructs if they exist
|
|
void COffscreenDrawable::delete_offscreen()
|
|
{
|
|
/* we should probably just mark ourselves purgable... */
|
|
DisposeGWorld ( mGWorld );
|
|
mGWorld = NULL;
|
|
mWidth = mHeight = 0;
|
|
mOwner = NULL;
|
|
}
|
|
|
|
COffscreenDrawable::~COffscreenDrawable()
|
|
{
|
|
delete_offscreen();
|
|
}
|
|
|
|
void
|
|
COffscreenDrawable::RelinquishDrawable(
|
|
CL_Drawable* inCLDrawable)
|
|
{
|
|
CDrawable::RelinquishDrawable(inCLDrawable);
|
|
|
|
// There are no clients left. Let's get rid of the offscreen
|
|
// bitmap.
|
|
if (mRefCnt == 0)
|
|
{
|
|
delete_offscreen();
|
|
}
|
|
}
|
|
|
|
// Called before using the drawable.
|
|
PRBool COffscreenDrawable::LockDrawable(
|
|
CL_Drawable* inCLDrawable,
|
|
CL_DrawableState inState)
|
|
{
|
|
if (inState == CL_UNLOCK_DRAWABLE)
|
|
{
|
|
return PR_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Check to see if this CL_Drawable was the last one to use
|
|
* this drawable. If not, someone else might have modified
|
|
* the bits since the last time this CL_Drawable wrote to
|
|
* to them.
|
|
*/
|
|
if ( (inState & CL_LOCK_DRAWABLE_FOR_READ) && (mOwner != inCLDrawable) )
|
|
{
|
|
return PR_FALSE;
|
|
}
|
|
|
|
mOwner = inCLDrawable;
|
|
|
|
if ( mGWorld == NULL )
|
|
{
|
|
return PR_FALSE;
|
|
}
|
|
|
|
LockPixels ( GetGWorldPixMap ( mGWorld ) );
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// Set the required dimensions of the drawable.
|
|
void
|
|
COffscreenDrawable::SetDimensions(int32 inWidth, int32 inHeight)
|
|
{
|
|
|
|
if ( (inWidth > mWidth) || (inHeight > mHeight) )
|
|
{
|
|
/*
|
|
* If there is only one client of the backing store,
|
|
* we can resize it to the dimensions specified.
|
|
* Otherwise, we can make it larger, but not smaller
|
|
*/
|
|
if (mRefCnt > 1)
|
|
{
|
|
if (inWidth < mWidth)
|
|
{
|
|
inWidth = mWidth;
|
|
}
|
|
|
|
if (inHeight < mHeight)
|
|
{
|
|
inHeight = mHeight;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create our offscreen. We need to try to find the deepest device that
|
|
* intersects the current screen area. Not sure how to do that at present.
|
|
* [hint: DeviceLoop]
|
|
*
|
|
* If a drawable is always set as the current drawable before its
|
|
* SetDimensions method is called, then we can have an explicit SetScreenArea
|
|
* method that allows us to track the current deepest screen that intersects
|
|
* us.
|
|
*
|
|
* We should also be able to keep the GWorld allocated and just reallocate the
|
|
* pixels rather than the whole damn thing. This will be a slight memory hit
|
|
* but a big speed win.
|
|
*/
|
|
|
|
OSErr err;
|
|
Rect r = { 0, 0, inHeight, inWidth };
|
|
GDHandle deepestDevice;
|
|
GDHandle gdList;
|
|
UInt32 deviceDepth;
|
|
UInt32 maxDepth;
|
|
Boolean maxIsColor;
|
|
|
|
/*
|
|
* Find the deepest device, or at least a color one
|
|
*/
|
|
maxIsColor = false;
|
|
maxDepth = 0;
|
|
deepestDevice = 0L;
|
|
gdList = GetDeviceList();
|
|
|
|
while ( gdList )
|
|
{
|
|
deviceDepth = (*(*gdList)->gdPMap)->pixelSize;
|
|
|
|
if ( deviceDepth > maxDepth )
|
|
{
|
|
// our new device is deeper, use it
|
|
maxDepth = deviceDepth;
|
|
maxIsColor = (*gdList)->gdFlags & 1;
|
|
deepestDevice = gdList;
|
|
}
|
|
else
|
|
if ( deviceDepth == maxDepth && !maxIsColor )
|
|
{
|
|
if ( ( (*gdList)->gdFlags & 1 ) != 0 )
|
|
{
|
|
// our new device is color, use it
|
|
maxIsColor = true;
|
|
deepestDevice = gdList;
|
|
}
|
|
}
|
|
|
|
gdList = GetNextDevice ( gdList );
|
|
}
|
|
|
|
Assert_ ( deepestDevice != NULL );
|
|
|
|
/* if we didn't find anything, then we just do an 8 bit color device */
|
|
if ( deepestDevice == NULL )
|
|
{
|
|
maxDepth = 8;
|
|
}
|
|
|
|
if ( mGWorld == NULL )
|
|
{
|
|
err = NewGWorld ( &mGWorld, maxDepth, &r, 0L, deepestDevice, noNewDevice + useTempMem );
|
|
}
|
|
else
|
|
{
|
|
err = UpdateGWorld ( &mGWorld, maxDepth, &r, 0L, deepestDevice, 0 );
|
|
}
|
|
|
|
if ( err != noErr )
|
|
{
|
|
mGWorld = NULL;
|
|
}
|
|
else
|
|
{
|
|
GDHandle gd;
|
|
CGrafPtr gp;
|
|
|
|
GetGWorld ( &gp, &gd );
|
|
SetGWorld ( mGWorld, NULL );
|
|
|
|
// DEBUG - so we know for sure when we draw unitialized pixels!
|
|
ForeColor ( redColor );
|
|
PaintRect ( &mGWorld->portRect );
|
|
|
|
SetGWorld ( gp, gd );
|
|
|
|
mWidth = inWidth;
|
|
mHeight = inHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* These are all our fe callback routines
|
|
*/
|
|
static PRBool fe_lock_drawable(CL_Drawable *drawable, CL_DrawableState state)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
return pDrawable->LockDrawable(drawable, state);
|
|
}
|
|
|
|
static void fe_init_drawable(CL_Drawable *drawable)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
pDrawable->InitDrawable(drawable);
|
|
}
|
|
|
|
static void fe_relinquish_drawable(CL_Drawable *drawable)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
pDrawable->RelinquishDrawable(drawable);
|
|
}
|
|
|
|
static void fe_set_drawable_origin(CL_Drawable *drawable, int32 x_offset, int32 y_offset)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
pDrawable->SetLayerOrigin(x_offset, y_offset);
|
|
}
|
|
|
|
static void fe_get_drawable_origin(CL_Drawable *drawable, int32 *x_offset, int32 *y_offset)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
pDrawable->GetLayerOrigin(x_offset, y_offset);
|
|
}
|
|
|
|
static void fe_set_drawable_clip(CL_Drawable *drawable, FE_Region clip_region)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
pDrawable->SetLayerClip(clip_region);
|
|
}
|
|
|
|
static void fe_restore_drawable_clip(CL_Drawable *drawable)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
pDrawable->SetLayerClip(NULL);
|
|
}
|
|
|
|
static void fe_copy_pixels(CL_Drawable *drawable_src, CL_Drawable *drawable_dest, FE_Region region)
|
|
{
|
|
CDrawable *pSrcDrawable = (CDrawable *)CL_GetDrawableClientData(drawable_src);
|
|
CDrawable *pDstDrawable = (CDrawable *)CL_GetDrawableClientData(drawable_dest);
|
|
|
|
pDstDrawable->CopyPixels(pSrcDrawable, region);
|
|
}
|
|
|
|
static void fe_set_drawable_dimensions(CL_Drawable *drawable, uint32 width, uint32 height)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
pDrawable->SetDimensions(width, height);
|
|
}
|
|
|
|
CL_DrawableVTable mfe_drawable_vtable = {
|
|
fe_lock_drawable,
|
|
fe_init_drawable,
|
|
fe_relinquish_drawable,
|
|
NULL,
|
|
fe_set_drawable_origin,
|
|
fe_get_drawable_origin,
|
|
fe_set_drawable_clip,
|
|
fe_restore_drawable_clip,
|
|
fe_copy_pixels,
|
|
fe_set_drawable_dimensions
|
|
};
|
|
|
|
|
|
#endif // LAYERS
|