Logo Search packages:      
Sourcecode: xulrunner-1.9 version File versions

nsRenderingContextBeOS.cpp

/* -*- 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>

static const pattern NS_BEOS_DASHED = { {0xc7, 0x8f, 0x1f, 0x3e, 0x7c, 0xf8, 0xf1, 0xe3} };
static const pattern NS_BEOS_DOTTED = { {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa} };

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);
      mCurrentBFont = nsnull;
      mCurrentLineStyle = nsLineStyle_kSolid;
      mCurrentLinePattern = B_SOLID_HIGH;
      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 (!mSurface)
            return rv;
      if (mView) 
            mView = nsnull;
      mSurface->AcquireView(&mView);
      if (!mView)
            return rv; 

      // Intelligent lock
      if (mSurface->LockDrawable()) 
      {
            // if BFont wasn't set already
            if (mCurrentBFont == nsnull)
            { 
                  if (mFontMetrics)
                        mFontMetrics->GetFontHandle((nsFontHandle&)mCurrentBFont);

                  if (mCurrentBFont)
                        mView->SetFont(mCurrentBFont);
                  else
                        mView->SetFont(be_plain_font); // fallback - only for single call
            }

            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; 
      if (LockAndUpdateView())
      {
            mView->SetHighColor(mRGB_color);
            UnlockView();
      }
      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)
{
      mCurrentBFont = nsnull;
      NS_IF_RELEASE(mFontMetrics);
      mFontMetrics = aFontMetrics;
      NS_IF_ADDREF(mFontMetrics);
      // Assigning value to mCurrentBFont and setting it as mView font
      if (LockAndUpdateView())
            UnlockView();
      return NS_OK;
}

NS_IMETHODIMP nsRenderingContextBeOS::SetLineStyle(nsLineStyle aLineStyle)
{
      switch(aLineStyle)
      {
            case nsLineStyle_kDashed:
                  mCurrentLinePattern = NS_BEOS_DASHED;
                  break;
            case nsLineStyle_kDotted:
                  mCurrentLinePattern = NS_BEOS_DOTTED;
                  break;
            case nsLineStyle_kSolid:
            default:
                  mCurrentLinePattern = B_SOLID_HIGH;
            break;
      }
      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), mCurrentLinePattern);
            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), mCurrentLinePattern);
                  }
                  else if (1 == w)
                  {
                        mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left, r.top + h -1), mCurrentLinePattern);
                  }
                  else
                  {
                        poly.MapTo(r,BRect(r.left, r.top, r.left + w -1, r.top + h - 1));
                        mView->StrokePolygon(&poly, false, mCurrentLinePattern);
                  }
                  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), mCurrentLinePattern);
                  else
                        mView->StrokeRect(BRect(x, y, x + w - 1, y + h - 1), mCurrentLinePattern);
                  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), mCurrentLinePattern);
                  }
                  else if (1 == w)
                  {
                        mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left, r.top + h -1), mCurrentLinePattern);
                  }
                  else
                  {
                        poly.MapTo(r,BRect(r.left, r.top, r.left + w -1, r.top + h - 1));
                        mView->StrokePolygon(&poly, true, mCurrentLinePattern);
                  }
                  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), mCurrentLinePattern);
            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, mCurrentLinePattern);
            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)
{
      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;
            // Using cached width if possible
            aWidth  = nscoord(((nsFontMetricsBeOS *)mFontMetrics)->GetStringWidth((char *)aString, aLength) * 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) 
            {
                  GetWidth(&aString[utf8pos[start]], numBytes, twWidth);
            } 

            // 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) 
                        {
                              GetWidth(&aString[utf8pos[start]], numBytes, twWidth);
                        }
                        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;
      
      if (LockAndUpdateView())  
      {
            PRBool doEmulateBold = PR_FALSE;
            
            if (mFontMetrics) 
            {
                  doEmulateBold = ((nsFontMetricsBeOS *)mFontMetrics)->IsBold() && !(mCurrentBFont->Face() & B_BOLD_FACE);
            }
            // XXX: B_OP_OVER isn't  most efficient for text rendering,
            // but it's the only way to render antialiased text correctly on arbitrary background
            PRBool offscreen;
            mSurface->IsOffscreen(&offscreen);
            mView->SetDrawingMode( offscreen ? B_OP_OVER : B_OP_COPY);
            if (nsnull == aSpacing || utf8_char_len((uchar)aString[0])==aLength) 
            {
                  mTranMatrix->TransformCoord(&xx, &yy);
                  mView->DrawString(aString, aLength, BPoint(xx, yy));
                  if (doEmulateBold)
                        mView->DrawString(aString, aLength, BPoint(xx + 1.0, 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));
                        if (doEmulateBold)
                              mView->DrawString((char *)(wpoint), ch_len, BPoint(xx + 1.0, 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);
      
      // XXX - No more use for that, should be removed in future
      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 *region = nsnull;
            if(mClipRegion && mSurface == aSrcSurf)
                  mClipRegion->GetNativeRegion((void *&)region);
            // Following else-code has sense only if srcview and destview frames
            // are equal and is incompatible with #define NOBBCACHE.
            // Keeping it here for fallback case.
            else if(srcview->Bounds() == destview->Bounds())
                  srcview->GetClippingRegion(region);
            destview->ConstrainClippingRegion(region);
      }
                        
                        // 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;
}

#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 || !mCurrentBFont)
            return NS_ERROR_FAILURE;

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


      GetWidth(aString, aLength, aBoundingMetrics.width );
      
      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 */

Generated by  Doxygen 1.6.0   Back to index