/* -*- 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 ); #if DEBUG // DEBUG - so we know for sure when we draw unitialized pixels! ForeColor ( redColor ); PaintRect ( &mGWorld->portRect ); #endif 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