mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
561 lines
15 KiB
C++
561 lines
15 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.
|
|
|
|
#include "stdafx.h"
|
|
|
|
#ifdef LAYERS
|
|
#include "drawable.h"
|
|
#include "xp_rect.h"
|
|
#include "cxprint.h"
|
|
|
|
// In this implementation, we only have a single offscreen drawable,
|
|
// and all its state is stored in the following statics:
|
|
HDC COffscreenDrawable::m_hDC = 0;
|
|
HPALETTE COffscreenDrawable::m_hSelectedPalette = 0;
|
|
COffscreenDrawable *COffscreenDrawable::m_pOffscreenDrawable = NULL;
|
|
HBITMAP COffscreenDrawable::m_hBitmap = 0;
|
|
CL_Drawable * COffscreenDrawable::m_pOwner = NULL;
|
|
int32 COffscreenDrawable::m_lWidth = 0;
|
|
int32 COffscreenDrawable::m_lHeight = 0;
|
|
UINT COffscreenDrawable::m_uRefCnt = 0;
|
|
|
|
// CDrawable base class
|
|
CDrawable::CDrawable()
|
|
{
|
|
m_lOrgX = m_lOrgY = 0;
|
|
m_hClipRgn = FE_NULL_REGION;
|
|
m_parentContext = NULL;
|
|
}
|
|
CDrawable::CDrawable(CAbstractCX* parentContext)
|
|
{
|
|
m_lOrgX = m_lOrgY = 0;
|
|
m_hClipRgn = FE_NULL_REGION;
|
|
m_parentContext = parentContext;
|
|
}
|
|
|
|
// Set and get the origin of the drawable
|
|
void
|
|
CDrawable::SetOrigin(int32 lOrgX, int32 lOrgY)
|
|
{
|
|
m_lOrgX = lOrgX;
|
|
m_lOrgY = lOrgY;
|
|
}
|
|
|
|
void
|
|
CDrawable::GetOrigin(int32 *lOrgX, int32 *lOrgY)
|
|
{
|
|
*lOrgX = m_lOrgX;
|
|
*lOrgY = m_lOrgY;
|
|
}
|
|
|
|
// Set the clip region of the drawable.
|
|
void
|
|
CDrawable::SetClip(FE_Region hClipRgn)
|
|
{
|
|
HDC hDC = GetDrawableDC();
|
|
ASSERT(hDC);
|
|
if (hDC) {
|
|
if (hClipRgn == FE_NULL_REGION)
|
|
::SelectClipRgn(hDC, NULL);
|
|
else
|
|
::SelectClipRgn(hDC, FE_GetMDRegion(hClipRgn));
|
|
m_hClipRgn = hClipRgn;
|
|
}
|
|
ReleaseDrawableDC(hDC);
|
|
}
|
|
|
|
typedef struct fe_CopyRectStruct {
|
|
HDC hSrcDC;
|
|
HDC hDstDC;
|
|
CAbstractCX* m_parentContext;
|
|
} fe_CopyRectStruct;
|
|
|
|
// For copying pixels, we draw a rectangle at a time
|
|
static void
|
|
fe_copy_rect_func(void *pRectStruct, XP_Rect *pRect)
|
|
{
|
|
#ifdef DDRAW
|
|
CAbstractCX* m_parentContext = ((fe_CopyRectStruct *)pRectStruct)->m_parentContext;
|
|
if (m_parentContext && m_parentContext->IsWindowContext()) {
|
|
CWinCX* pWinCX = (CWinCX*)m_parentContext;
|
|
if ( pWinCX->GetPrimarySurface()) {
|
|
LTRB rect;
|
|
rect.left = pRect->left;
|
|
rect.top = pRect->top;
|
|
rect.right = pRect->right;
|
|
rect.bottom = pRect->bottom;
|
|
pWinCX->BltToScreen(rect);
|
|
return;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
HDC hSrcDC = ((fe_CopyRectStruct *)pRectStruct)->hSrcDC;
|
|
HDC hDstDC = ((fe_CopyRectStruct *)pRectStruct)->hDstDC;
|
|
|
|
BitBlt(hDstDC, CASTINT(pRect->left), CASTINT(pRect->top),
|
|
CASTINT(pRect->right-pRect->left),
|
|
CASTINT(pRect->bottom-pRect->top),
|
|
hSrcDC,
|
|
CASTINT(pRect->left), CASTINT(pRect->top),
|
|
SRCCOPY);
|
|
}
|
|
|
|
void
|
|
CDrawable::CopyPixels(CDrawable *pSrcDrawable, FE_Region hCopyRgn)
|
|
{
|
|
HDC hSrcDC;
|
|
HDC hDstDC = GetDrawableDC();
|
|
fe_CopyRectStruct rectStruct;
|
|
|
|
ASSERT(hDstDC);
|
|
|
|
if (hDstDC && pSrcDrawable && (hCopyRgn != FE_NULL_REGION)) {
|
|
hSrcDC = pSrcDrawable->GetDrawableDC();
|
|
|
|
if (hSrcDC) {
|
|
#ifndef XP_WIN32
|
|
// On Win16, we must BLT through the clip, instead of iterating
|
|
// through the region's rectangles, due to Win16's inability
|
|
// to decompose a region into its component rectangles.
|
|
SetClip(hCopyRgn);
|
|
#endif
|
|
rectStruct.hDstDC = hDstDC;
|
|
rectStruct.hSrcDC = hSrcDC;
|
|
rectStruct.m_parentContext = m_parentContext;
|
|
// The following statement is misleading, because on
|
|
// Win16, FE_ForEachRectInRegion() calls its client
|
|
// function only once, using the bounding box of the
|
|
// region, not with each individual rectangle in the
|
|
// region.
|
|
FE_ForEachRectInRegion(hCopyRgn,
|
|
(FE_RectInRegionFunc)fe_copy_rect_func,
|
|
(void *)&rectStruct);
|
|
#ifndef XP_WIN32
|
|
SetClip(NULL);
|
|
#endif
|
|
|
|
ReleaseDrawableDC(hSrcDC);
|
|
}
|
|
ReleaseDrawableDC(hDstDC);
|
|
}
|
|
}
|
|
|
|
|
|
COnscreenDrawable::COnscreenDrawable(HDC hDC, CAbstractCX* parentContext)
|
|
:CDrawable(parentContext)
|
|
{
|
|
m_hDC = hDC;
|
|
}
|
|
|
|
CPrinterDrawable::CPrinterDrawable(HDC hDC,
|
|
int32 lLeftMargin,
|
|
int32 lRightMargin,
|
|
int32 lTopMargin,
|
|
int32 lBottomMargin,
|
|
CAbstractCX* parentContext)
|
|
{
|
|
m_hDC = hDC;
|
|
m_lLeftMargin = lLeftMargin;
|
|
m_lRightMargin = lRightMargin;
|
|
m_lTopMargin = lTopMargin;
|
|
m_lBottomMargin = lBottomMargin;
|
|
m_parentContext = parentContext;
|
|
}
|
|
|
|
void
|
|
CPrinterDrawable::SetClip(FE_Region hClipRgn)
|
|
{
|
|
XP_Rect bbox;
|
|
|
|
if (hClipRgn != FE_NULL_REGION) {
|
|
FE_Region hTempClipRgn;
|
|
RECT rect;
|
|
|
|
/* Get region bounding box and add in the margins */
|
|
FE_GetRegionBoundingBox(hClipRgn, &bbox);
|
|
XP_OffsetRect(&bbox, m_lLeftMargin, m_lTopMargin);
|
|
|
|
rect.left = bbox.left;
|
|
rect.right = bbox.right;
|
|
rect.top = bbox.top;
|
|
rect.bottom = bbox.bottom;
|
|
|
|
::LPtoDP(m_hDC, (LPPOINT)&rect, 2);
|
|
|
|
bbox.left = rect.left;
|
|
bbox.right = rect.right;
|
|
bbox.top = rect.top;
|
|
bbox.bottom = rect.bottom;
|
|
|
|
/*
|
|
* Create, set and then destroy a region based on the offset and
|
|
* scaled bounding box.
|
|
*/
|
|
hTempClipRgn = FE_CreateRectRegion(&bbox);
|
|
|
|
CDrawable::SetClip(hTempClipRgn);
|
|
if (hTempClipRgn)
|
|
FE_DestroyRegion(hTempClipRgn);
|
|
#ifdef XP_WIN32
|
|
CPrintCX* pContext = (CPrintCX*)m_parentContext;
|
|
HDC m_offscrnDC = pContext->GetOffscreenDC();
|
|
|
|
if (m_offscrnDC) {
|
|
FE_GetRegionBoundingBox(hClipRgn, &bbox);
|
|
CPrintCX* prntContext = (CPrintCX*)m_parentContext;
|
|
bbox.left = (bbox.left +prntContext->GetXConvertUnit())/ prntContext->GetXConvertUnit();
|
|
bbox.right = (bbox.right +prntContext->GetYConvertUnit())/ prntContext->GetXConvertUnit();
|
|
bbox.top = (bbox.top + prntContext->GetXConvertUnit())/ prntContext->GetYConvertUnit();
|
|
bbox.bottom = (bbox.bottom + prntContext->GetYConvertUnit()) / prntContext->GetYConvertUnit();
|
|
hTempClipRgn = FE_CreateRectRegion(&bbox);
|
|
if (hTempClipRgn == FE_NULL_REGION)
|
|
::SelectClipRgn(m_offscrnDC, NULL);
|
|
else
|
|
::SelectClipRgn(m_offscrnDC, FE_GetMDRegion(hTempClipRgn));
|
|
if (hTempClipRgn)
|
|
FE_DestroyRegion(hTempClipRgn);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
CDrawable::SetClip(FE_NULL_REGION);
|
|
}
|
|
|
|
|
|
// We should get rid of this function, since it does nothing
|
|
// that the new operator does not.
|
|
COffscreenDrawable *
|
|
COffscreenDrawable::AllocateOffscreen(HDC hParentDC, HPALETTE hPal, CAbstractCX* parentContext)
|
|
{
|
|
return new COffscreenDrawable(hParentDC, hPal, parentContext);
|
|
}
|
|
|
|
COffscreenDrawable::COffscreenDrawable(HDC hParentDC, HPALETTE hPal, CAbstractCX* parentContext)
|
|
:CDrawable(parentContext)
|
|
{
|
|
m_hParentDC = hParentDC;
|
|
m_hSaveBitmap = NULL;
|
|
m_hSavePalette = NULL;
|
|
m_hParentPal = hPal;
|
|
m_parentContext = parentContext;
|
|
}
|
|
// Set the clip region of the drawable.
|
|
void
|
|
COffscreenDrawable::SetClip(FE_Region hClipRgn)
|
|
{
|
|
#ifdef DDRAW
|
|
if (m_parentContext && m_parentContext->IsWindowContext()){
|
|
CWinCX *pwincx = (CWinCX*)m_parentContext;
|
|
if (pwincx->GetBackSurface()) {
|
|
pwincx->SetClipOnDrawSurface(pwincx->GetBackSurface(), FE_GetMDRegion(hClipRgn));
|
|
}
|
|
}
|
|
else
|
|
CDrawable::SetClip(hClipRgn);
|
|
#else
|
|
CDrawable::SetClip(hClipRgn);
|
|
#endif
|
|
m_hClipRgn = hClipRgn;
|
|
}
|
|
HDC COffscreenDrawable::GetDrawableDC()
|
|
{
|
|
if (m_hDC)
|
|
return m_hDC;
|
|
else { // we must be using DirectDraw's offscreen surface.
|
|
CWinCX *pwincx = (CWinCX*)m_parentContext;
|
|
|
|
return pwincx->GetDispDC();
|
|
}
|
|
}
|
|
void COffscreenDrawable::ReleaseDrawableDC(HDC hdc)
|
|
{
|
|
}
|
|
|
|
// Get rid of any offscreen constructs if they exist
|
|
void
|
|
COffscreenDrawable::delete_offscreen()
|
|
{
|
|
if (m_hDC) {
|
|
::SelectObject(m_hDC, m_hSaveBitmap);
|
|
::SelectPalette(m_hDC, (HPALETTE)GetStockObject(DEFAULT_PALETTE), FALSE);
|
|
m_hSelectedPalette = 0;
|
|
DeleteDC(m_hDC);
|
|
}
|
|
|
|
if (m_hBitmap)
|
|
::DeleteObject(m_hBitmap);
|
|
|
|
m_hDC = NULL;
|
|
m_hSaveBitmap = NULL;
|
|
m_hSavePalette = NULL;
|
|
m_hBitmap = NULL;
|
|
m_lWidth = m_lHeight = 0;
|
|
}
|
|
|
|
COffscreenDrawable::~COffscreenDrawable()
|
|
{
|
|
// It's possible that the window that "owns" this offscreen
|
|
// drawable is about to be deleted, along with its palette. We
|
|
// have to deselect the palette from this drawable's DC so that
|
|
// it can be safely deleted.
|
|
if (m_hSelectedPalette && (m_hParentPal == m_hSelectedPalette)) {
|
|
::SelectPalette(m_hDC, (HPALETTE)GetStockObject(DEFAULT_PALETTE), FALSE);
|
|
m_hSelectedPalette = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
COffscreenDrawable::InitDrawable(CL_Drawable *pCLDrawable)
|
|
{
|
|
m_uRefCnt++;
|
|
}
|
|
|
|
void
|
|
COffscreenDrawable::RelinquishDrawable(CL_Drawable *pCLDrawable)
|
|
{
|
|
ASSERT(m_uRefCnt > 0);
|
|
|
|
if (m_uRefCnt)
|
|
m_uRefCnt--;
|
|
|
|
// There are no clients left. Let's get rid of the offscreen
|
|
// bitmap.
|
|
if (m_uRefCnt == 0)
|
|
delete_offscreen();
|
|
}
|
|
|
|
// Called before using the drawable.
|
|
PRBool
|
|
COffscreenDrawable::LockDrawable(CL_Drawable *pCLDrawable,
|
|
CL_DrawableState newState)
|
|
{
|
|
|
|
if (newState == CL_UNLOCK_DRAWABLE)
|
|
return PR_TRUE;
|
|
|
|
CWinCX* pWinCX = (CWinCX*)m_parentContext;
|
|
#ifdef DDRAW
|
|
if (!pWinCX->GetBackSurface() && !m_hBitmap)
|
|
return PR_FALSE;
|
|
#else
|
|
if (!m_hBitmap)
|
|
return PR_FALSE;
|
|
#endif
|
|
/*
|
|
* 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 ((newState & CL_LOCK_DRAWABLE_FOR_READ) &&
|
|
(m_pOwner != pCLDrawable))
|
|
return PR_FALSE;
|
|
|
|
m_pOwner = pCLDrawable;
|
|
|
|
//mwh - If we are using directDraw, don't do the palette stuff..
|
|
// If the last user of this drawable relinquished it, then
|
|
// we need to select the new user's palette into the DC.
|
|
#ifdef DDRAW
|
|
if (!(pWinCX->GetBackSurface()) && !m_hSelectedPalette && m_hParentPal) {
|
|
#else
|
|
if (!m_hSelectedPalette && m_hParentPal) {
|
|
#endif
|
|
::SelectPalette(m_hDC, m_hParentPal, FALSE);
|
|
m_hSelectedPalette = m_hParentPal;
|
|
}
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// Set the required dimensions of the drawable.
|
|
void
|
|
COffscreenDrawable::SetDimensions(int32 lWidth, int32 lHeight)
|
|
{
|
|
|
|
if ((lWidth > m_lWidth) || (lHeight > m_lHeight)) {
|
|
|
|
/*
|
|
* 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 (m_uRefCnt > 1) {
|
|
if (lWidth < m_lWidth)
|
|
lWidth = m_lWidth;
|
|
if (lHeight < m_lHeight)
|
|
lHeight = m_lHeight;
|
|
}
|
|
|
|
/* If we already have a bitmap, get rid of it */
|
|
if (m_hBitmap && m_hDC) {
|
|
::SelectObject(m_hDC, m_hSaveBitmap);
|
|
::DeleteObject(m_hBitmap);
|
|
m_hBitmap = NULL;
|
|
}
|
|
CWinCX* pWinCX = (CWinCX*)m_parentContext;
|
|
|
|
//mwh - If we are using directDraw, don't create a compatible DC here.
|
|
#ifdef DDRAW
|
|
if (!(pWinCX->GetBackSurface())) {
|
|
#endif
|
|
if (!m_hDC) {
|
|
m_hDC = ::CreateCompatibleDC(m_hParentDC);
|
|
if (!m_hDC)
|
|
return;
|
|
|
|
SetPalette(m_hParentPal);
|
|
::SetBkMode(m_hDC, TRANSPARENT);
|
|
}
|
|
|
|
m_hBitmap = ::CreateCompatibleBitmap(m_hParentDC,
|
|
CASTINT(lWidth), CASTINT(lHeight));
|
|
if (!m_hBitmap) {
|
|
/*
|
|
* If the bitmap allocation failed, we just delete all offscreen
|
|
* resources and fail locking and hence, go fall back to
|
|
* onscreen drawing.
|
|
*/
|
|
delete_offscreen();
|
|
}
|
|
m_hSaveBitmap = (HBITMAP)::SelectObject(m_hDC, m_hBitmap);
|
|
#ifdef DDRAW
|
|
}
|
|
#endif
|
|
m_lWidth = lWidth;
|
|
m_lHeight = lHeight;
|
|
}
|
|
}
|
|
|
|
void
|
|
COffscreenDrawable::SetPalette(HPALETTE hPal)
|
|
{
|
|
CWinCX* pWinCX = (CWinCX*)m_parentContext;
|
|
#ifdef DDRAW
|
|
if (!(pWinCX->GetBackSurface())) {
|
|
#endif
|
|
HPALETTE hOldPalette = NULL;
|
|
|
|
if(hPal && m_hDC) {
|
|
hOldPalette = ::SelectPalette(m_hDC, hPal, FALSE);
|
|
// don't realize palette with memory DC.
|
|
m_hSelectedPalette = hPal;
|
|
}
|
|
|
|
if (!m_hSavePalette)
|
|
m_hSavePalette = hOldPalette;
|
|
m_hParentPal = hPal;
|
|
#ifdef DDRAW
|
|
}
|
|
#endif
|
|
}
|
|
|
|
PRBool
|
|
fe_lock_drawable(CL_Drawable *drawable, CL_DrawableState state)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
|
|
return pDrawable->LockDrawable(drawable, state);
|
|
}
|
|
|
|
void
|
|
fe_init_drawable(CL_Drawable *drawable)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
|
|
pDrawable->InitDrawable(drawable);
|
|
}
|
|
|
|
void
|
|
fe_relinquish_drawable(CL_Drawable *drawable)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
|
|
pDrawable->RelinquishDrawable(drawable);
|
|
}
|
|
|
|
void
|
|
fe_set_drawable_origin(CL_Drawable *drawable, int32 lOrgX, int32 lOrgY)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
|
|
pDrawable->SetOrigin(lOrgX, lOrgY);
|
|
}
|
|
|
|
void
|
|
fe_get_drawable_origin(CL_Drawable *drawable, int32 *plOrgX, int32 *plOrgY)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
|
|
pDrawable->GetOrigin(plOrgX, plOrgY);
|
|
}
|
|
|
|
void
|
|
fe_set_drawable_clip(CL_Drawable *drawable, FE_Region hClipRgn)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
|
|
pDrawable->SetClip(hClipRgn);
|
|
}
|
|
|
|
void
|
|
fe_restore_drawable_clip(CL_Drawable *drawable)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
|
|
pDrawable->SetClip(NULL);
|
|
}
|
|
|
|
void
|
|
fe_copy_pixel(CL_Drawable *pSrc, CL_Drawable *pDst, FE_Region hCopyRgn)
|
|
{
|
|
CDrawable *pSrcDrawable = (CDrawable *)CL_GetDrawableClientData(pSrc);
|
|
CDrawable *pDstDrawable = (CDrawable *)CL_GetDrawableClientData(pDst);
|
|
|
|
pDstDrawable->CopyPixels(pSrcDrawable, hCopyRgn);
|
|
}
|
|
|
|
void
|
|
fe_drawable_set_dimensions(CL_Drawable *drawable, uint32 lWidth, uint32 lHeight)
|
|
{
|
|
CDrawable *pDrawable = (CDrawable *)CL_GetDrawableClientData(drawable);
|
|
|
|
pDrawable->SetDimensions(lWidth, lHeight);
|
|
}
|
|
|
|
CL_DrawableVTable wfe_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_pixel,
|
|
fe_drawable_set_dimensions
|
|
};
|
|
|
|
#endif // LAYERS
|