/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Daniel Switkin and Mathias Agopian
 *   Sergei Dolgov           <sergei_d@fi.tartu.ee>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nsFontMetricsBeOS.h"
#include "nsRenderingContextBeOS.h"
#include "nsRegionBeOS.h"
#include "nsImageBeOS.h"
#include "nsGraphicsStateBeOS.h"
#include "nsICharRepresentable.h"
#include "prenv.h"
#include <Polygon.h>
#include <math.h>


NS_IMPL_ISUPPORTS1(nsRenderingContextBeOS, nsIRenderingContext)

static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);

nsRenderingContextBeOS::nsRenderingContextBeOS()
{
	mOffscreenSurface = nsnull;
	mSurface = nsnull;
	mContext = nsnull;
	mFontMetrics = nsnull;
	mClipRegion = nsnull;
	mStateCache = new nsVoidArray();
	mView = nsnull;	
	mCurrentColor = NS_RGB(255, 255, 255);
	mCurrentFont = nsnull;
	mCurrentLineStyle = nsLineStyle_kSolid;
	mP2T = 1.0f;
	mTranMatrix = nsnull;

	PushState();
}

nsRenderingContextBeOS::~nsRenderingContextBeOS()
{
	// Destroy the State Machine
	if (mStateCache)
	{
		PRInt32 cnt = mStateCache->Count();
		while (--cnt >= 0)
			PopState();
		delete mStateCache;
		mStateCache = nsnull;
	}
	
	delete mTranMatrix;
	NS_IF_RELEASE(mOffscreenSurface);
	NS_IF_RELEASE(mFontMetrics);
	NS_IF_RELEASE(mContext);
}


NS_IMETHODIMP nsRenderingContextBeOS::Init(nsIDeviceContext *aContext, nsIWidget *aWindow)
{	
	if (!aContext || !aWindow)
		return NS_ERROR_NULL_POINTER;

	BView *view = (BView *)aWindow->GetNativeData(NS_NATIVE_GRAPHIC);
	if (!view)
		return NS_ERROR_FAILURE;
	
	mSurface = new nsDrawingSurfaceBeOS();
	if (!mSurface)
		return NS_ERROR_OUT_OF_MEMORY;
	
	mContext = aContext;
	NS_IF_ADDREF(mContext);

	mSurface->Init(view);
	mOffscreenSurface = mSurface;
	NS_ADDREF(mSurface);
	return CommonInit();
}

NS_IMETHODIMP nsRenderingContextBeOS::Init(nsIDeviceContext *aContext, nsIDrawingSurface* aSurface)
{
	if (!aContext || !aSurface)
		return NS_ERROR_NULL_POINTER;
		
	mContext = aContext;
	NS_IF_ADDREF(mContext);
	mSurface = (nsDrawingSurfaceBeOS *) aSurface;
	mOffscreenSurface = mSurface;
	NS_ADDREF(mSurface);
	return CommonInit();
}

NS_IMETHODIMP nsRenderingContextBeOS::CommonInit()
{
	if (!mTranMatrix)
		return NS_ERROR_OUT_OF_MEMORY;

	mP2T = mContext->DevUnitsToAppUnits();
	float app2dev;
	app2dev = mContext->AppUnitsToDevUnits();
 	mTranMatrix->AddScale(app2dev, app2dev);
	return NS_OK;
}

// We like PRUnichar rendering, hopefully it's not slowing us too much
NS_IMETHODIMP nsRenderingContextBeOS::GetHints(PRUint32 &aResult)
{
	aResult = 0;
	if (!PR_GetEnv("MOZILLA_GFX_DISABLE_FAST_MEASURE"))
		aResult = NS_RENDERING_HINT_FAST_MEASURE;	
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::LockDrawingSurface(PRInt32 aX, PRInt32 aY, PRUint32 aWidth,
	PRUint32 aHeight, void **aBits, PRInt32 *aStride, PRInt32 *aWidthBytes, PRUint32 aFlags)
{
	PushState();
	return mSurface->Lock(aX, aY, aWidth, aHeight, aBits, aStride, aWidthBytes, aFlags);
}

NS_IMETHODIMP nsRenderingContextBeOS::UnlockDrawingSurface()
{
	PopState();
	mSurface->Unlock();
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::SelectOffScreenDrawingSurface(nsIDrawingSurface* aSurface)
{
	if (nsnull == aSurface)
		mSurface = mOffscreenSurface;
	else
		mSurface = (nsDrawingSurfaceBeOS *)aSurface;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetDrawingSurface(nsIDrawingSurface* *aSurface)
{
	*aSurface = mSurface;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::Reset()
{
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetDeviceContext(nsIDeviceContext *&aContext)
{
	NS_IF_ADDREF(mContext);
	aContext = mContext;
	return NS_OK;
}

// Get a new GS
NS_IMETHODIMP nsRenderingContextBeOS::PushState()
{
#ifdef USE_GS_POOL
	nsGraphicsState *state = nsGraphicsStatePool::GetNewGS();
#else
	nsGraphicsState *state = new nsGraphicsState;
#endif
	// Push into this state object, add to vector
	if (!state)
		return NS_ERROR_OUT_OF_MEMORY;

	nsTransform2D *tranMatrix;
	if (nsnull == mTranMatrix)
		tranMatrix = new nsTransform2D();
	else
		tranMatrix = new nsTransform2D(mTranMatrix);

	if (!tranMatrix)
	{
#ifdef USE_GS_POOL
		nsGraphicsStatePool::ReleaseGS(state);
#else
		delete state;
#endif
		return NS_ERROR_OUT_OF_MEMORY;
	}
	state->mMatrix = mTranMatrix;
	mTranMatrix = tranMatrix;

	// Set state to mClipRegion. SetClip{Rect,Region}() will do copy-on-write stuff
	state->mClipRegion = mClipRegion;

	NS_IF_ADDREF(mFontMetrics);
	state->mFontMetrics = mFontMetrics;
	state->mColor = mCurrentColor;
	state->mLineStyle = mCurrentLineStyle;

	mStateCache->AppendElement(state);
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::PopState(void)
{
	PRUint32 cnt = mStateCache->Count();
	nsGraphicsState *state;
	
	if (cnt > 0)
	{
		state = (nsGraphicsState *)mStateCache->ElementAt(cnt - 1);
		mStateCache->RemoveElementAt(cnt - 1);
		
		// Assign all local attributes from the state object just popped
		if (state->mMatrix)
		{
			delete mTranMatrix;
			mTranMatrix = state->mMatrix;
		}
		
		mClipRegion = state->mClipRegion;
		if (state->mFontMetrics && (mFontMetrics != state->mFontMetrics))
			SetFont(state->mFontMetrics);
		if (state->mColor != mCurrentColor)
			SetColor(state->mColor);
		if (state->mLineStyle != mCurrentLineStyle)
			SetLineStyle(state->mLineStyle);

		// Delete this graphics state object
#ifdef USE_GS_POOL
		nsGraphicsStatePool::ReleaseGS(state);
#else
		delete state;
#endif
	}
	
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::IsVisibleRect(const nsRect& aRect, PRBool &aVisible)
{
	aVisible = PR_TRUE;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetClipRect(nsRect &aRect, PRBool &aClipValid)
{
	if (!mClipRegion)
		return NS_ERROR_FAILURE;
	if (!mClipRegion->IsEmpty())
	{
		PRInt32 x, y, w, h;
		mClipRegion->GetBoundingBox(&x, &y, &w, &h);
		aRect.SetRect(x, y, w, h);
		aClipValid = PR_TRUE;
	}
	else
	{
		aRect.SetRect(0, 0, 0, 0);
		aClipValid = PR_FALSE;
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::SetClipRect(const nsRect& aRect, nsClipCombine aCombine)
{
	
	PRUint32 cnt = mStateCache->Count();
	nsGraphicsState *state = nsnull;
	if (cnt > 0)
		state = (nsGraphicsState *)mStateCache->ElementAt(cnt - 1);
	
	if (state && mClipRegion && state->mClipRegion == mClipRegion)
	{
		nsCOMPtr<nsIRegion> region;
		GetClipRegion(getter_AddRefs(region));
		mClipRegion = region;
	}
	
	CreateClipRegion();
	nsRect trect = aRect;
	mTranMatrix->TransformCoord(&trect.x, &trect.y, &trect.width, &trect.height);

	switch (aCombine)
	{
		case nsClipCombine_kIntersect:
			mClipRegion->Intersect(trect.x, trect.y, trect.width, trect.height);
			break;
		case nsClipCombine_kUnion:
			mClipRegion->Union(trect.x, trect.y, trect.width, trect.height);
			break;
		case nsClipCombine_kSubtract:
			mClipRegion->Subtract(trect.x, trect.y, trect.width, trect.height);
			break;
		case nsClipCombine_kReplace:
			mClipRegion->SetTo(trect.x, trect.y, trect.width, trect.height);
			break;
	}
	
	return NS_OK;
} 

// To reduce locking overhead, the caller must unlock the looper itself.
// TO DO: Locking and unlocking around each graphics primitive is still very lame
// and imposes serious overhead. Now we fixed this problem for offscreen surfaces,
// but there are also BViews "imported" from nsWidget
bool nsRenderingContextBeOS::LockAndUpdateView() 
{
	bool rv = false;
	if (mView) 
		mView = nsnull;
	
	mSurface->AcquireView(&mView);
	// Intelligent lock
	if (mView && mSurface->LockDrawable()) 
	{
		if (mCurrentFont == nsnull) 
			mCurrentFont = (BFont *)be_plain_font;
	
		mView->SetFont(mCurrentFont);
		mView->SetHighColor(mRGB_color);

		if (mClipRegion) 
		{
			BRegion *region = nsnull;
			mClipRegion->GetNativeRegion((void *&)region);
			mView->ConstrainClippingRegion(region);
		}
		else
		{
			mView->ConstrainClippingRegion(0);
		}
		rv = true;
	}
	return rv;
}

void nsRenderingContextBeOS::UnlockView()
{
	mSurface->UnlockDrawable();
}
	
void nsRenderingContextBeOS::CreateClipRegion()
{
	// We have 3 cases to deal with:
	//  1 - There is no mClipRegion -> Create one
	//  2 - There is an mClipRegion shared w/ stack -> Duplicate and unshare
	//  3 - There is an mClipRegion and its not shared -> return

	if (mClipRegion)
	{
		PRUint32 cnt = mStateCache->Count();

		if (cnt == 0) 
			return;

		nsGraphicsState *state;
		state = (nsGraphicsState *)mStateCache->ElementAt(cnt - 1);

		if (state->mClipRegion != mClipRegion)
			return;
			
		mClipRegion = new nsRegionBeOS;
		if (mClipRegion)
			mClipRegion->SetTo(*state->mClipRegion);
	}
	else
	{
		PRUint32 w, h;
		mSurface->GetSize(&w, &h);

		mClipRegion = new nsRegionBeOS;
		if (mClipRegion)
		{
			mClipRegion->Init();
			mClipRegion->SetTo(0, 0, w, h);
		}
	}
}
	
NS_IMETHODIMP nsRenderingContextBeOS::SetClipRegion(const nsIRegion &aRegion, nsClipCombine aCombine)
{
	
	PRUint32 cnt = mStateCache->Count();
	nsGraphicsState *state = nsnull;
	if (cnt > 0)
		state = (nsGraphicsState *)mStateCache->ElementAt(cnt - 1);
	
	if (state && mClipRegion && state->mClipRegion == mClipRegion)
	{
		nsCOMPtr<nsIRegion> region;
		GetClipRegion(getter_AddRefs(region));
		mClipRegion = region;
	}
	
	CreateClipRegion();
	switch (aCombine)
	{
		case nsClipCombine_kIntersect:
			mClipRegion->Intersect(aRegion);
			break;
		case nsClipCombine_kUnion:
			mClipRegion->Union(aRegion);
			break;
		case nsClipCombine_kSubtract:
			mClipRegion->Subtract(aRegion);
			break;
		case nsClipCombine_kReplace:
			mClipRegion->SetTo(aRegion);
			break;
	}
	
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::CopyClipRegion(nsIRegion &aRegion)
{
	if (!mClipRegion)
		return NS_ERROR_FAILURE;
	aRegion.SetTo(*mClipRegion);
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetClipRegion(nsIRegion **aRegion)
{
	if (!aRegion || !mClipRegion)
		return NS_ERROR_NULL_POINTER;
	
	if (*aRegion) // copy it, they should be using CopyClipRegion
	{
		(*aRegion)->SetTo(*mClipRegion);
	}
	else
	{
		nsCOMPtr<nsIRegion> newRegion = new nsRegionBeOS();
		newRegion->Init();
		newRegion->SetTo(*mClipRegion);
		NS_ADDREF(*aRegion = newRegion);
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::SetColor(nscolor aColor) 
{
	if (nsnull == mContext)
		return NS_ERROR_FAILURE;
	mCurrentColor = aColor;
	mRGB_color.red = NS_GET_R(mCurrentColor);
	mRGB_color.green = NS_GET_G(mCurrentColor);
	mRGB_color.blue = NS_GET_B(mCurrentColor);
	mRGB_color.alpha = 255;	
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetColor(nscolor &aColor) const 
{
	aColor = mCurrentColor;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::SetFont(const nsFont &aFont, nsIAtom* aLangGroup)
{
	nsCOMPtr<nsIFontMetrics> newMetrics;
	nsresult rv = mContext->GetMetricsFor(aFont, aLangGroup, *getter_AddRefs(newMetrics));
	if (NS_SUCCEEDED(rv)) 
		rv = SetFont(newMetrics);
	return rv;
}

NS_IMETHODIMP nsRenderingContextBeOS::SetFont(nsIFontMetrics *aFontMetrics)
{
	NS_IF_RELEASE(mFontMetrics);
	mFontMetrics = aFontMetrics;
	NS_IF_ADDREF(mFontMetrics);
	
	if (mFontMetrics)
	{
		nsFontHandle fontHandle;
		mFontMetrics->GetFontHandle(fontHandle);
		mCurrentFont = (BFont *)fontHandle;
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::SetLineStyle(nsLineStyle aLineStyle)
{
	// TODO: BeOS Line Style. Maybe using patterns.
	mCurrentLineStyle = aLineStyle;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetLineStyle(nsLineStyle &aLineStyle)
{
	aLineStyle = mCurrentLineStyle;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetFontMetrics(nsIFontMetrics *&aFontMetrics)
{
	NS_IF_ADDREF(mFontMetrics);
	aFontMetrics = mFontMetrics;
	return NS_OK;
}

// Add the passed in translation to the current translation
NS_IMETHODIMP nsRenderingContextBeOS::Translate(nscoord aX, nscoord aY)
{
	mTranMatrix->AddTranslation((float)aX, (float)aY);
	return NS_OK;
}

// Add the passed in scale to the current scale
NS_IMETHODIMP nsRenderingContextBeOS::Scale(float aSx, float aSy)
{
	mTranMatrix->AddScale(aSx, aSy);
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetCurrentTransform(nsTransform2D *&aTransform)
{
	aTransform = mTranMatrix;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::CreateDrawingSurface(const nsRect& aBounds, PRUint32 aSurfFlags,
	nsIDrawingSurface* &aSurface)
{
	
	if (nsnull == mSurface)
	{
		aSurface = nsnull;
		return NS_ERROR_FAILURE;
	}
	
	if ((aBounds.width <= 0) || (aBounds.height <= 0))
		return NS_ERROR_FAILURE;
	
	nsDrawingSurfaceBeOS *surf = new nsDrawingSurfaceBeOS();
	if (!surf)
	{
		aSurface = nsnull;
		return NS_ERROR_FAILURE;
	}

	NS_ADDREF(surf);
	// Getting drawable and updating context
	if (LockAndUpdateView())
		UnlockView();
		
	surf->Init(mView, aBounds.width, aBounds.height, aSurfFlags);
	aSurface = surf;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::DestroyDrawingSurface(nsIDrawingSurface* aDS)
{
	nsDrawingSurfaceBeOS *surf = (nsDrawingSurfaceBeOS *)aDS;
	if (surf == nsnull)
		return NS_ERROR_FAILURE;
	NS_IF_RELEASE(surf);
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawLine(nscoord aX0, nscoord aY0, nscoord aX1, nscoord aY1) {
	if (mTranMatrix == nsnull)
		return NS_ERROR_FAILURE;
	if (mSurface == nsnull)
		return NS_ERROR_FAILURE;
	
	mTranMatrix->TransformCoord(&aX0, &aY0);
	mTranMatrix->TransformCoord(&aX1, &aY1);
	nscoord diffX = aX1 - aX0;
	nscoord diffY = aY1 - aY0;
	
	if (0 != diffX)
		diffX = (diffX > 0) ? 1 : -1;
	if (0 != diffY)
		diffY = (diffY > 0) ? 1 : -1;
	
	
	if (LockAndUpdateView())
	{
		mView->StrokeLine(BPoint(aX0, aY0), BPoint(aX1 - diffX, aY1 - diffY));
		UnlockView();
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawPolyline(const nsPoint aPoints[], PRInt32 aNumPoints)
{
	if (mTranMatrix == nsnull)
		return NS_ERROR_FAILURE;
	if (mSurface == nsnull)
		return NS_ERROR_FAILURE;
	BPoint *pts;
	BPolygon poly;
	BRect r;
	PRInt32 w, h;
	//allocating from stack if amount isn't big
	BPoint bpointbuf[64];
	pts = bpointbuf;
	if (aNumPoints>64)
		pts = new BPoint[aNumPoints];
	for (int i = 0; i < aNumPoints; ++i)
	{
		nsPoint p = aPoints[i];
		mTranMatrix->TransformCoord(&p.x, &p.y);
		pts[i].x = p.x;
		pts[i].y = p.y;
#ifdef DEBUG
		printf("polyline(%i,%i)\n", p.x, p.y);
#endif
	}
	poly.AddPoints(pts, aNumPoints);
	r = poly.Frame();	
	w = r.IntegerWidth();
	h = r.IntegerHeight();
//	Don't draw empty polygon
	if (w && h)
	{
		if (LockAndUpdateView())
		{
			if (1 == h) 
			{
				mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left + w - 1, r.top));
			}
			else if (1 == w)
			{
				mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left, r.top + h -1));
			}
			else
			{
				poly.MapTo(r,BRect(r.left, r.top, r.left + w -1, r.top + h - 1));
				mView->StrokePolygon(&poly, false);
			}
			UnlockView();
		}		
	}	
	if (pts!=bpointbuf)
		delete [] pts;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawRect(const nsRect& aRect)
{
	return DrawRect(aRect.x, aRect.y, aRect.width, aRect.height);
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawRect(nscoord aX, nscoord aY, nscoord aWidth,
	nscoord aHeight)
{
	
	if (nsnull == mTranMatrix || nsnull == mSurface)
		return NS_ERROR_FAILURE;

	// After the transform, if the numbers are huge, chop them, because
	// they're going to be converted from 32 bit to 16 bit.
	// It's all way off the screen anyway.
	nscoord x = aX, y = aY, w = aWidth, h = aHeight;
	mTranMatrix->TransformCoord(&x, &y, &w, &h);
	ConditionRect(x, y, w, h);
	
	// Don't draw empty rectangles; also, w/h are adjusted down by one
	// so that the right number of pixels are drawn.
	if (w && h)
	{
		if (LockAndUpdateView())
		{
			// FIXME: add line style
			if (1 == h)
				mView->StrokeLine(BPoint(x, y), BPoint(x + w - 1, y));
			else
				mView->StrokeRect(BRect(x, y, x + w - 1, y + h - 1));
			UnlockView();
		}
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::FillRect(const nsRect &aRect)
{
	return FillRect(aRect.x, aRect.y, aRect.width, aRect.height);
}

NS_IMETHODIMP nsRenderingContextBeOS::FillRect(nscoord aX, nscoord aY, nscoord aWidth,
	nscoord aHeight)
{
	
	if (nsnull == mTranMatrix || nsnull == mSurface)
		return NS_ERROR_FAILURE;
	
	// After the transform, if the numbers are huge, chop them, because
	// they're going to be converted from 32 bit to 16 bit.
	// It's all way off the screen anyway.
	nscoord x = aX, y = aY, w = aWidth, h = aHeight;
	mTranMatrix->TransformCoord(&x, &y, &w, &h);
	ConditionRect(x, y, w, h);
	
	if (w && h)
	{
		if (LockAndUpdateView())
		{
			// FIXME: add line style
			if (1 == h)
				mView->StrokeLine(BPoint(x, y), BPoint(x + w - 1, y));
			else
				mView->FillRect(BRect(x, y, x + w - 1, y + h - 1), B_SOLID_HIGH);
			UnlockView();
		}
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::InvertRect(const nsRect &aRect)
{
	return InvertRect(aRect.x, aRect.y, aRect.width, aRect.height);
}

NS_IMETHODIMP nsRenderingContextBeOS::InvertRect(nscoord aX, nscoord aY, nscoord aWidth,
	nscoord aHeight)
{
	
	if (nsnull == mTranMatrix || nsnull == mSurface)
		return NS_ERROR_FAILURE;
	
	// After the transform, if the numbers are huge, chop them, because
	// they're going to be converted from 32 bit to 16 bit.
	// It's all way off the screen anyway.
	nscoord x = aX, y = aY, w = aWidth, h = aHeight;
	mTranMatrix->TransformCoord(&x, &y, &w, &h);
	ConditionRect(x, y, w, h);
	
	if (w && h)
	{
		if (LockAndUpdateView())
		{
			//Mozilla doesn't seem to set clipping for InvertRect, so we do it here - bug 230267
			BRegion tmpClip = BRegion(BRect(x, y, x + w - 1, y + h - 1));
			mView->ConstrainClippingRegion(&tmpClip);
			mView->InvertRect(BRect(x, y, x + w - 1, y + h - 1));
			mView->Sync();
			UnlockView();
		}
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawPolygon(const nsPoint aPoints[], PRInt32 aNumPoints)
{
	if (nsnull == mTranMatrix || nsnull == mSurface)
		return NS_ERROR_FAILURE;
	BPoint *pts;
	BPolygon poly;
	BRect r;
	PRInt32 w, h;
	//allocating from stack if amount isn't big
	BPoint bpointbuf[64];
	pts = bpointbuf;
	if (aNumPoints>64)
		pts = new BPoint[aNumPoints];
	for (int i = 0; i < aNumPoints; ++i)
	{
		nsPoint p = aPoints[i];
		mTranMatrix->TransformCoord(&p.x, &p.y);
		pts[i].x = p.x;
		pts[i].y = p.y;
	}
	poly.AddPoints(pts, aNumPoints);
	r = poly.Frame();	
	w = r.IntegerWidth();
	h = r.IntegerHeight();
//	Don't draw empty polygon
	if (w && h)
	{
		if (LockAndUpdateView())
		{
			if (1 == h)
			{
				mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left + w - 1, r.top));
			}
			else if (1 == w)
			{
				mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left, r.top + h -1));
			}
			else
			{
				poly.MapTo(r,BRect(r.left, r.top, r.left + w -1, r.top + h - 1));
				mView->StrokePolygon(&poly, true, B_SOLID_HIGH);
			}
			UnlockView();
		}		
	}		
	if (pts!=bpointbuf)
		delete [] pts;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::FillPolygon(const nsPoint aPoints[], PRInt32 aNumPoints)
{
	if (nsnull == mTranMatrix || nsnull == mSurface)
		return NS_ERROR_FAILURE;
	
	BPoint *pts;
	BPolygon poly;
	BRect r;
	PRInt32 w, h;
	BPoint bpointbuf[64];
	pts = bpointbuf;
	if (aNumPoints>64)
		pts = new BPoint[aNumPoints];
	for (int i = 0; i < aNumPoints; ++i)
	{
		nsPoint p = aPoints[i];
		mTranMatrix->TransformCoord(&p.x, &p.y);
		pts[i].x = p.x;
		pts[i].y = p.y;
	}
	poly.AddPoints(pts, aNumPoints);
	r = poly.Frame();
	w = r.IntegerWidth();
	h = r.IntegerHeight();
//	Don't draw empty polygon
	if (w && h)
	{
		if (LockAndUpdateView())
		{
			if (1 == h)
			{
				mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left + w - 1, r.top));
			}
			else if (1 == w)
			{
				mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left, r.top + h -1));
			}
			else
			{
				poly.MapTo(r,BRect(r.left, r.top, r.left + w -1, r.top + h - 1));
				mView->FillPolygon(&poly, B_SOLID_HIGH);
			}
			UnlockView();
		}		
	}
	if (pts!=bpointbuf)
		delete [] pts;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawEllipse(const nsRect &aRect)
{
	return DrawEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
{
	if (nsnull == mTranMatrix || nsnull == mSurface)
		return NS_ERROR_FAILURE;

	nscoord x = aX, y = aY, w = aWidth, h = aHeight;
	mTranMatrix->TransformCoord(&x, &y, &w, &h);
	
	if (LockAndUpdateView())
	{
		mView->StrokeEllipse(BRect(x, y, x + w - 1, y + h - 1));
		UnlockView();
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::FillEllipse(const nsRect &aRect)
{
	return FillEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
}

NS_IMETHODIMP nsRenderingContextBeOS::FillEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
{
	if (nsnull == mTranMatrix || nsnull == mSurface)
		return NS_ERROR_FAILURE;

	nscoord x = aX, y = aY, w = aWidth, h = aHeight;
	mTranMatrix->TransformCoord(&x, &y, &w, &h);
	
	if (LockAndUpdateView())
	{
		mView->FillEllipse(BRect(x, y, x + w - 1, y + h - 1));
		UnlockView();
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawArc(const nsRect& aRect, float aStartAngle, float aEndAngle)
{
	return DrawArc(aRect.x, aRect.y, aRect.width, aRect.height, aStartAngle, aEndAngle);
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
	float aStartAngle, float aEndAngle)
{
	if (nsnull == mTranMatrix || nsnull == mSurface)
		return NS_ERROR_FAILURE;

	nscoord x = aX, y = aY, w = aWidth, h = aHeight;
	mTranMatrix->TransformCoord(&x, &y, &w, &h);
	
	if (LockAndUpdateView())
	{
		// FIXME: add line style
		mView->StrokeArc(BRect(x, y, x + w - 1, y + h - 1), aStartAngle, aEndAngle - aStartAngle);
		UnlockView();
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::FillArc(const nsRect &aRect, float aStartAngle, float aEndAngle)
{
	return FillArc(aRect.x, aRect.y, aRect.width, aRect.height, aStartAngle, aEndAngle);
}

NS_IMETHODIMP nsRenderingContextBeOS::FillArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
	float aStartAngle, float aEndAngle)
{
	if (nsnull == mTranMatrix || nsnull == mSurface)
		return NS_ERROR_FAILURE;

	nscoord x = aX, y = aY, w = aWidth, h = aHeight;
	mTranMatrix->TransformCoord(&x, &y, &w, &h);
	
	if (LockAndUpdateView())
	{
		mView->FillArc(BRect(x, y, x + w - 1, y + h - 1), aStartAngle, aEndAngle - aStartAngle);
		UnlockView();
	}
	return NS_OK;
}

// Block of UTF-8 helpers
// Whole block of utf-8 helpers must  be placed before any gfx text method
// in order to allow text handling directly from PRUnichar* methods in future

// From BeNewsLetter #82:
// Get the number of character bytes for a given utf8 byte character
inline uint32 utf8_char_len(uchar byte) 
{
	return (((0xE5000000 >> ((byte >> 3) & 0x1E)) & 3) + 1);
}

// useful UTF-8 utilities 

#define BEGINS_CHAR(byte) ((byte & 0xc0) != 0x80)

inline uint32 utf8_str_len(const char* ustring) 
{
	uint32 cnt = 0;
	while ( *ustring != '\0')
	{
		if ( BEGINS_CHAR( *ustring ) )
			++cnt;
			++ustring;
	}
	return cnt;       
}

// Macro to convert a ushort* uni_string into a char* or uchar* utf8_string,
// one character at a time. Move the pointer. You can check terminaison on
// the uni_string by testing : if (uni_string[0] == 0)
// WARNING : you need to use EXPLICIT POINTERS for both str and unistr.
#define convert_to_utf8(str, uni_str) {\
	if ((uni_str[0]&0xff80) == 0)\
		*str++ = *uni_str++;\
	else if ((uni_str[0]&0xf800) == 0) {\
		str[0] = 0xc0|(uni_str[0]>>6);\
		str[1] = 0x80|((*uni_str++)&0x3f);\
		str += 2;\
	} else if ((uni_str[0]&0xfc00) != 0xd800) {\
		str[0] = 0xe0|(uni_str[0]>>12);\
		str[1] = 0x80|((uni_str[0]>>6)&0x3f);\
		str[2] = 0x80|((*uni_str++)&0x3f);\
		str += 3;\
	} else {\
		int val;\
		val = ((uni_str[0]-0xd7c0)<<10) | (uni_str[1]&0x3ff);\
		str[0] = 0xf0 | (val>>18);\
		str[1] = 0x80 | ((val>>12)&0x3f);\
		str[2] = 0x80 | ((val>>6)&0x3f);\
		str[3] = 0x80 | (val&0x3f);\
		uni_str += 2; str += 4;\
	}\
}

// End of block of UTF-8 helpers

NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(char aC, nscoord &aWidth)
{
	// Check for the very common case of trying to get the width of a single space
	if ((aC == ' ') && (nsnull != mFontMetrics))
		return mFontMetrics->GetSpaceWidth(aWidth);
	else
		return GetWidth(&aC, 1, aWidth);
	}

NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(PRUnichar aC, nscoord &aWidth, PRInt32 *aFontID)
{
	return GetWidth(&aC, 1, aWidth, aFontID);
}

NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const nsString &aString, nscoord& aWidth, PRInt32 *aFontID)
{
	return GetWidth(aString.get(), aString.Length(), aWidth, aFontID);
}

NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const char *aString, nscoord &aWidth)
{
	return GetWidth(aString, strlen(aString), aWidth);
}

NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const char *aString, PRUint32 aLength, nscoord &aWidth)
{
	
	if (0 == aLength)
	{
		aWidth = 0;
	}
	else
	{
		if (aString == nsnull)
			return NS_ERROR_FAILURE;
		PRUint32 rawWidth = (PRUint32)mCurrentFont->StringWidth(aString, aLength);
		aWidth = NSToCoordRound(rawWidth * mP2T);
	}
	return NS_OK;
}


NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const PRUnichar *aString, PRUint32 aLength,
	nscoord &aWidth, PRInt32 *aFontID)
{
	// max UTF-8 string length
	uint8 *utf8str = new uint8[aLength * 4 + 1];
	uint8 *utf8ptr = utf8str;
	const PRUnichar *uniptr = aString;
	
	for (PRUint32 i = 0; i < aLength; i++)
	{
		convert_to_utf8(utf8ptr, uniptr);
	}
	
	*utf8ptr = '\0';
	uint32 utf8str_len = strlen((char *)utf8str);
	GetWidth((char *)utf8str, utf8str_len, aWidth);
	delete [] utf8str;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const char *aString, PRUint32 aLength,
	nsTextDimensions &aDimensions)
{
	if (mFontMetrics)
	{
		mFontMetrics->GetMaxAscent(aDimensions.ascent);
		mFontMetrics->GetMaxDescent(aDimensions.descent);
	}	
	return GetWidth(aString, aLength, aDimensions.width);
}

NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const PRUnichar *aString, PRUint32 aLength,
	nsTextDimensions &aDimensions, PRInt32 *aFontID)
{
	if (mFontMetrics)
	{
		mFontMetrics->GetMaxAscent(aDimensions.ascent);
		mFontMetrics->GetMaxDescent(aDimensions.descent);
	}	
	return GetWidth(aString, aLength, aDimensions.width, aFontID);
}

// FAST TEXT MEASURE methods
// Implementation is simplicistic in comparison with other platforms - we follow in this method
// generic BeOS-port approach for string methods in GFX - convert from PRUnichar* to (UTF-8) char*
// and call (UTF-8) char* version of the method.
// It works well with current nsFontMetricsBeOS which is, again, simpler in comparison with
// other platforms, partly due to UTF-8 nature of BeOS, partly due unimplemented font fallbacks
// and other tricks.

NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const PRUnichar* aString,
	PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, PRInt32 aNumBreaks,
	nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions,
	PRInt32* aFontID = nsnull)
{
	nsresult ret_code = NS_ERROR_FAILURE;	
	uint8 utf8buf[1024];
	uint8* utf8str = nsnull;
	// max UTF-8 string length
	PRUint32 slength = aLength * 4 + 1;
	// Allocating char* array rather from stack than from heap for speed.
	//  1024 char array forms  e.g.  256 == 32*8 frame for CJK glyphs, which may be
	// insufficient for purpose of this method, but until we implement storage
	//in nsSurface, i think it is good compromise.
	if (slength < 1024) 
		utf8str = utf8buf;
	else 
		utf8str = new uint8[slength];

	uint8 *utf8ptr = utf8str;
	const PRUnichar *uniptr = aString;
	
	for (PRUint32 i = 0; i < aLength; ++i) 
		convert_to_utf8(utf8ptr, uniptr);
	
	*utf8ptr = '\0';
	ret_code = GetTextDimensions((char *)utf8str, utf8ptr-utf8str, aAvailWidth, aBreaks, aNumBreaks,
                               aDimensions, aNumCharsFit, aLastWordDimensions, aFontID);
	// deallocating if got from heap
	if (utf8str != utf8buf)
			delete [] utf8str;
	return ret_code;
}

NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const char* aString, PRInt32 aLength,
	PRInt32 aAvailWidth,PRInt32* aBreaks, PRInt32 aNumBreaks, nsTextDimensions& aDimensions,
	PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, PRInt32* aFontID = nsnull)
{
	// Code is borrowed from win32 implementation including comments.
	// Minor changes are introduced due multibyte/utf-8 nature of char* strings handling in BeOS.
	char * utf8ptr = (char *)aString;
	PRInt32 *utf8pos =0;	
	PRInt32 num_of_glyphs = 0; // Number of glyphs isn't equal to number of bytes in case of UTF-8
	PRInt32 utf8posbuf[1025];
	if (aLength < 1025) 
		utf8pos = utf8posbuf;
	else 
		utf8pos = new PRInt32[aLength + 1];

	// counting number of glyphs (not bytes) in utf-8 string 
	//and recording positions of first byte for each utf-8 char
	PRInt32 i = 0;
	while (i < aLength)
	{
		if ( BEGINS_CHAR( utf8ptr[i] ) )
		{
			utf8pos[num_of_glyphs] = i;
			++num_of_glyphs;
		}
		i++;
	}	
	utf8pos[num_of_glyphs] = i; // IMPORTANT for non-ascii strings for proper last-string-in-block.
	NS_PRECONDITION(aBreaks[aNumBreaks - 1] == num_of_glyphs, "invalid break array");
	
	// If we need to back up this state represents the last place we could
	// break. We can use this to avoid remeasuring text
	PRInt32 prevBreakState_BreakIndex = -1; // not known (hasn't been computed)
	nscoord prevBreakState_Width = 0; // accumulated width to this point
	mFontMetrics->GetMaxAscent(aLastWordDimensions.ascent);
	mFontMetrics->GetMaxDescent(aLastWordDimensions.descent);
	aLastWordDimensions.width = -1;
	aNumCharsFit = 0;
	// Iterate each character in the string and determine which font to use
	nscoord width = 0;
	PRInt32 start = 0;
	nscoord aveCharWidth;
	PRInt32 numBytes = 0;
	// allocating  array for positions of first bytes of utf-8 chars in utf-8 string
	// from stack if possible
	
	mFontMetrics->GetAveCharWidth(aveCharWidth);


	while (start < num_of_glyphs) 
	{
		// Estimate how many characters will fit. Do that by diving the available
		// space by the average character width. Make sure the estimated number
		// of characters is at least 1
		PRInt32 estimatedNumChars = 0;
		if (aveCharWidth > 0) 
			estimatedNumChars = (aAvailWidth - width) / aveCharWidth;

		if (estimatedNumChars < 1) 
			estimatedNumChars = 1;

		// Find the nearest break offset
		PRInt32 estimatedBreakOffset = start + estimatedNumChars;
		PRInt32 breakIndex;
		nscoord numChars;
		if (num_of_glyphs <= estimatedBreakOffset) 
		{
			// All the characters should fit
			numChars = num_of_glyphs - start;
			// BeOS specifics - getting number of bytes from position array. Same for all remaining numBytes occurencies.
			numBytes = aLength - utf8pos[start];
			breakIndex = aNumBreaks - 1;
		}
		else 
		{
			breakIndex = prevBreakState_BreakIndex;
			while (((breakIndex + 1) < aNumBreaks) 
					&& (aBreaks[breakIndex + 1] <= estimatedBreakOffset)) 
			{
				++breakIndex;
			}
			
			if (breakIndex == prevBreakState_BreakIndex) 
				++breakIndex; // make sure we advanced past the previous break index

			numChars = aBreaks[breakIndex] - start;
			numBytes = utf8pos[aBreaks[breakIndex]] - utf8pos[start];
		}
		nscoord twWidth = 0;
		if ((1 == numChars) && (aString[utf8pos[start]] == ' ')) 
		{
			mFontMetrics->GetSpaceWidth(twWidth);
		} 
		else if (numChars > 0) 
		{
			float  size = mCurrentFont->StringWidth(&aString[utf8pos[start]], numBytes);
			twWidth = NSToCoordRound(size * mP2T);
		} 

		// See if the text fits
		PRBool  textFits = (twWidth + width) <= aAvailWidth;
		// If the text fits then update the width and the number of
		// characters that fit
		if (textFits) 
		{
			aNumCharsFit += numChars;
			width += twWidth;
			start += numChars;

			// This is a good spot to back up to if we need to so remember
			// this state
			prevBreakState_BreakIndex = breakIndex;
			prevBreakState_Width = width;
		}
		else 
		{
			// See if we can just back up to the previous saved state and not
			// have to measure any text
			if (prevBreakState_BreakIndex > 0) 
			{
				// If the previous break index is just before the current break index
				// then we can use it
				if (prevBreakState_BreakIndex == (breakIndex - 1)) 
				{
					aNumCharsFit = aBreaks[prevBreakState_BreakIndex];
					width = prevBreakState_Width;
					break;
				}
			}
			// We can't just revert to the previous break state
			if (0 == breakIndex)
			{
				// There's no place to back up to, so even though the text doesn't fit
				// return it anyway
				aNumCharsFit += numChars;
				width += twWidth;
				break;
			}       
			// Repeatedly back up until we get to where the text fits or we're all
			// the way back to the first word
			width += twWidth;
			while ((breakIndex >= 1) && (width > aAvailWidth)) 
			{
				twWidth = 0;
				start = aBreaks[breakIndex - 1];
				numChars = aBreaks[breakIndex] - start;
				numBytes = utf8pos[aBreaks[breakIndex]] - utf8pos[start];
				if ((1 == numChars) && (aString[utf8pos[start]] == ' ')) 
				{
					mFontMetrics->GetSpaceWidth(twWidth);
				}
				else if (numChars > 0) 
				{
					float size = mCurrentFont->StringWidth(&aString[utf8pos[start]], numBytes);
					twWidth = NSToCoordRound(size * mP2T);
				}
				width -= twWidth;
				aNumCharsFit = start;
				--breakIndex;
			}
		break;   
		}	       
	}
	aDimensions.width = width;
	mFontMetrics->GetMaxAscent(aDimensions.ascent);
	mFontMetrics->GetMaxDescent(aDimensions.descent);
	// deallocating if got from heap
	if(utf8pos != utf8posbuf) 
		delete utf8pos;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawString(const nsString &aString,
	nscoord aX, nscoord aY, PRInt32 aFontID, const nscoord *aSpacing)
{
	return DrawString(aString.get(), aString.Length(), aX, aY, aFontID, aSpacing);
}


// TO DO: A better solution is needed for both antialiasing as noted below and
// character spacing - these are both suboptimal.
NS_IMETHODIMP nsRenderingContextBeOS::DrawString(const char *aString, PRUint32 aLength,
	nscoord aX, nscoord aY, const nscoord *aSpacing)
{
	
	if (0 == aLength)
		return NS_OK; 

	if (mTranMatrix == nsnull)
		return NS_ERROR_FAILURE;
	if (mSurface == nsnull)
		return NS_ERROR_FAILURE;
	if (aString == nsnull)
		return NS_ERROR_FAILURE;

	nscoord xx = aX, yy = aY, y=aY;
	
	// Subtract xFontStruct ascent since drawing specifies baseline
	
	if (LockAndUpdateView())  
	{
		// XXX: the following maybe isn't  most efficient for text rendering,
		// but it's the easy way to render antialiased text correctly
		mView->SetDrawingMode(B_OP_OVER);
		if (nsnull == aSpacing || utf8_char_len((uchar)aString[0])==aLength) 
		{
			mTranMatrix->TransformCoord(&xx, &yy);
			mView->DrawString(aString, aLength, BPoint(xx, yy));
		}
		else 
		{
			char *wpoint =0;
			int32 unichnum=0,  position=aX, ch_len=0;
			for (PRUint32 i =0; i <= aLength; i += ch_len)
			{
				ch_len = utf8_char_len((uchar)aString[i]);
				wpoint = (char *)(aString + i);
				xx = position; 
				yy = y;
				mTranMatrix->TransformCoord(&xx, &yy);
				// yy++; DrawString quirk!
				mView->DrawString((char *)(wpoint), ch_len, BPoint(xx, yy)); 
				position += aSpacing[unichnum++];
			}
		}
		mView->SetDrawingMode(B_OP_COPY);
		UnlockView();
	}
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::DrawString(const PRUnichar *aString, PRUint32 aLength,
	nscoord aX, nscoord aY, PRInt32 aFontID, const nscoord *aSpacing)
{
	
	// max UTF-8 string length
	uint8 *utf8str = new uint8[aLength * 4 + 1];
	uint8 *utf8ptr = utf8str;
	const PRUnichar *uniptr = aString;
	
	for (PRUint32 i = 0; i < aLength; i++) 
		convert_to_utf8(utf8ptr, uniptr);
	
	*utf8ptr = '\0';
	uint32 utf8str_len = strlen((char *)utf8str);
	DrawString((char *)utf8str, utf8str_len, aX, aY, aSpacing);
	delete [] utf8str;
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::CopyOffScreenBits(nsIDrawingSurface* aSrcSurf,
	PRInt32 aSrcX, PRInt32 aSrcY, const nsRect &aDestBounds, PRUint32 aCopyFlags)
{
	
	PRInt32 srcX = aSrcX;
	PRInt32 srcY = aSrcY;
	nsRect drect = aDestBounds;
	
	if (aSrcSurf == nsnull)
		return NS_ERROR_FAILURE;
	if (mTranMatrix == nsnull)
		return NS_ERROR_FAILURE;
	if (mSurface == nsnull)
		return NS_ERROR_FAILURE;
		
	BView *srcview = nsnull;
	BView *destview = nsnull;
	BBitmap *srcbitmap = nsnull;
	nsDrawingSurfaceBeOS *srcsurf = nsnull;
	nsDrawingSurfaceBeOS *destsurf = nsnull;
	
	srcsurf = (nsDrawingSurfaceBeOS *)aSrcSurf;
	srcsurf->AcquireView(&srcview);
	srcsurf->AcquireBitmap(&srcbitmap);
	
			
	if (aCopyFlags & NS_COPYBITS_TO_BACK_BUFFER) 
	{
		NS_ASSERTION(nsnull != mSurface, "no back buffer");
		destsurf = mSurface;
	} 
	else 
	{
		destsurf = mOffscreenSurface;
	}
			
	if (!srcbitmap && srcsurf != mOffscreenSurface)
	{
#ifdef DEBUG
    		printf("nsRenderingContextBeOS::CopyOffScreenBits - FIXME: should render from surface without bitmap!?!?!\n");
#endif
		if (srcview)
			srcsurf->ReleaseView();
		return NS_OK;
	}
	
	destsurf->AcquireView(&destview);

	if (!destview)
	{
#ifdef DEBUG
    		printf("nsRenderingContextBeOS::CopyOffScreenBits - FIXME: no BView to draw!?!?!\n");
#endif
    		return NS_OK;
	}

	if (aCopyFlags & NS_COPYBITS_XFORM_SOURCE_VALUES) 
		mTranMatrix->TransformCoord(&srcX, &srcY);
 	
	if (aCopyFlags & NS_COPYBITS_XFORM_DEST_VALUES)
					mTranMatrix->TransformCoord(&drect.x, &drect.y, &drect.width, &drect.height);

	if (!LockAndUpdateView())
	{
#ifdef DEBUG
		printf("nsRenderingContextBeOS::CopyOffScreenBits - FIXME: no mVviewView - LockAndUpdate failed!\n");
#endif
    		return NS_OK;
	}
	// Locking for safety surface which wasn't locked above
	if (srcsurf != mSurface)
		srcsurf->LockDrawable();
	if (destsurf != mSurface)
		destsurf->LockDrawable();
	// Since all the drawing in this class is asynchronous, we must synchronize with
	// the app_server before drawing its contents anywhere else
	if (srcview)
		srcview->Sync();

	if (aCopyFlags & NS_COPYBITS_USE_SOURCE_CLIP_REGION) 
	{
		BRegion r;
		srcview->GetClippingRegion(&r);
		destview->ConstrainClippingRegion(&r);
	}
				
				// Draw to destination synchronously to make sure srcbitmap doesn't change
				// before the blit is finished.
	// TODO: look if we can blit directly in case of rendering to BDirectWindow, another buffer or self
	destview->DrawBitmap(srcbitmap, BRect(srcX, srcY, srcX + drect.width - 1, srcY + drect.height - 1),
		BRect(drect.x, drect.y, drect.x + drect.width - 1, drect.y + drect.height - 1));
	
	if (destsurf != mSurface)
		destsurf->UnlockDrawable();
	if (srcsurf != mSurface)
		srcsurf->UnlockDrawable();
	UnlockView();
	// Release. Fake at the moment.
	destsurf->ReleaseView();	
	srcsurf->ReleaseBitmap();	
	srcsurf->ReleaseView();
	return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::RetrieveCurrentNativeGraphicData(PRUint32 *ngd)
{
	return NS_OK;
}



#ifdef MOZ_MATHML
  /**
   * Returns metrics (in app units) of an 8-bit character string
   */
NS_IMETHODIMP 
nsRenderingContextBeOS::GetBoundingMetrics(const char* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics)
{
	aBoundingMetrics.Clear();
	if (0 >= aLength || !aString || !mCurrentFont)
		return NS_ERROR_FAILURE;

	BRect rect;
	escapement_delta delta;
	delta.nonspace = 0;
	delta.space = 0;
	// Use the printing metric to get more detail
	mCurrentFont->GetBoundingBoxesForStrings(&aString, 1, B_PRINTING_METRIC, &delta, &rect);


	aBoundingMetrics.width = NSToCoordRound(mCurrentFont->StringWidth(aString) * mP2T);
	
	aBoundingMetrics.leftBearing = NSToCoordRound(rect.left * mP2T);
	aBoundingMetrics.rightBearing = NSToCoordRound(rect.right * mP2T);

	// The pen position for DrawString is at the baseline of the font for BeOS.  
	// The orientation for drawing moves downward for vertical metrics.
	// MathML expects what X Windows does: the orientation of the ascent moves upward from the baseline.
	// So, we need to negate the top value returned by GetBoundingBoxesForStrings, to have that value
	// move up in BeOS.
	aBoundingMetrics.ascent = NSToCoordRound((-rect.top) * mP2T);
	aBoundingMetrics.descent = NSToCoordRound(rect.bottom * mP2T);

	return NS_OK;
}

  /**
   * Returns metrics (in app units) of a Unicode character string
   */
NS_IMETHODIMP 
nsRenderingContextBeOS::GetBoundingMetrics(const PRUnichar* aString, PRUint32 aLength,
	nsBoundingMetrics& aBoundingMetrics, PRInt32* aFontID) 
{
	aBoundingMetrics.Clear();
	nsresult r = NS_OK;
	if (0 < aLength)
	{
		if (aString == NULL)
			return NS_ERROR_FAILURE;

		// Since more often than not, single char strings are passed to this function
		// we will try keep this on the stack, instead of the heap
		uint8 utf8buf[1024];
		uint8* utf8str = (uint8*)&utf8buf;
		if (aLength * 4 + 1 > 1024)
			utf8str = new uint8[aLength * 4 + 1];
		uint8 *utf8ptr = utf8str;
		const PRUnichar *uniptr = aString;
	
		for (PRUint32 i = 0; i < aLength; i++)
		{
			convert_to_utf8(utf8ptr, uniptr);
		}
	
		*utf8ptr = '\0';
		uint32 utf8str_len = strlen((char *)utf8str);
		r = GetBoundingMetrics((char *)utf8str, utf8str_len, aBoundingMetrics);
		if (utf8str != utf8buf)
			delete [] utf8str;
		if (nsnull != aFontID)
			*aFontID = 0;
	}		
	return r;
}
#endif /* MOZ_MATHML */
