Back to index

lightning-sunbird  0.9+nobinonly
nsRenderingContextBeOS.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Daniel Switkin and Mathias Agopian
00024  *   Sergei Dolgov           <sergei_d@fi.tartu.ee>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsFontMetricsBeOS.h"
00041 #include "nsRenderingContextBeOS.h"
00042 #include "nsRegionBeOS.h"
00043 #include "nsImageBeOS.h"
00044 #include "nsGraphicsStateBeOS.h"
00045 #include "nsICharRepresentable.h"
00046 #include "prenv.h"
00047 #include <Polygon.h>
00048 #include <math.h>
00049 
00050 static const pattern dashes = { {0xc7, 0x8f, 0x1f, 0x3e, 0x7c, 0xf8, 0xf1, 0xe3} };
00051 static const pattern dots = { {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa} };
00052 
00053 NS_IMPL_ISUPPORTS1(nsRenderingContextBeOS, nsIRenderingContext)
00054 
00055 static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
00056 
00057 nsRenderingContextBeOS::nsRenderingContextBeOS()
00058 {
00059        mOffscreenSurface = nsnull;
00060        mSurface = nsnull;
00061        mContext = nsnull;
00062        mFontMetrics = nsnull;
00063        mClipRegion = nsnull;
00064        mStateCache = new nsVoidArray();
00065        mView = nsnull;      
00066        mCurrentColor = NS_RGB(255, 255, 255);
00067        mCurrentBFont = nsnull;
00068        mCurrentLineStyle = nsLineStyle_kSolid;
00069        mCurrentLinePattern = B_SOLID_HIGH;
00070        mP2T = 1.0f;
00071        mTranMatrix = nsnull;
00072 
00073        PushState();
00074 }
00075 
00076 nsRenderingContextBeOS::~nsRenderingContextBeOS()
00077 {
00078        // Destroy the State Machine
00079        if (mStateCache)
00080        {
00081               PRInt32 cnt = mStateCache->Count();
00082               while (--cnt >= 0)
00083                      PopState();
00084               delete mStateCache;
00085               mStateCache = nsnull;
00086        }
00087        
00088        delete mTranMatrix;
00089        NS_IF_RELEASE(mOffscreenSurface);
00090        NS_IF_RELEASE(mFontMetrics);
00091        NS_IF_RELEASE(mContext);
00092 }
00093 
00094 
00095 NS_IMETHODIMP nsRenderingContextBeOS::Init(nsIDeviceContext *aContext, nsIWidget *aWindow)
00096 {      
00097        if (!aContext || !aWindow)
00098               return NS_ERROR_NULL_POINTER;
00099 
00100        BView *view = (BView *)aWindow->GetNativeData(NS_NATIVE_GRAPHIC);
00101        if (!view)
00102               return NS_ERROR_FAILURE;
00103        
00104        mSurface = new nsDrawingSurfaceBeOS();
00105        if (!mSurface)
00106               return NS_ERROR_OUT_OF_MEMORY;
00107        
00108        mContext = aContext;
00109        NS_IF_ADDREF(mContext);
00110 
00111        mSurface->Init(view);
00112        mOffscreenSurface = mSurface;
00113        NS_ADDREF(mSurface);
00114        return CommonInit();
00115 }
00116 
00117 NS_IMETHODIMP nsRenderingContextBeOS::Init(nsIDeviceContext *aContext, nsIDrawingSurface* aSurface)
00118 {
00119        if (!aContext || !aSurface)
00120               return NS_ERROR_NULL_POINTER;
00121               
00122        mContext = aContext;
00123        NS_IF_ADDREF(mContext);
00124        mSurface = (nsDrawingSurfaceBeOS *) aSurface;
00125        mOffscreenSurface = mSurface;
00126        NS_ADDREF(mSurface);
00127        return CommonInit();
00128 }
00129 
00130 NS_IMETHODIMP nsRenderingContextBeOS::CommonInit()
00131 {
00132        if (!mTranMatrix)
00133               return NS_ERROR_OUT_OF_MEMORY;
00134 
00135        mP2T = mContext->DevUnitsToAppUnits();
00136        float app2dev;
00137        app2dev = mContext->AppUnitsToDevUnits();
00138        mTranMatrix->AddScale(app2dev, app2dev);
00139        return NS_OK;
00140 }
00141 
00142 // We like PRUnichar rendering, hopefully it's not slowing us too much
00143 NS_IMETHODIMP nsRenderingContextBeOS::GetHints(PRUint32 &aResult)
00144 {
00145        aResult = 0;
00146        if (!PR_GetEnv("MOZILLA_GFX_DISABLE_FAST_MEASURE"))
00147               aResult = NS_RENDERING_HINT_FAST_MEASURE; 
00148        return NS_OK;
00149 }
00150 
00151 NS_IMETHODIMP nsRenderingContextBeOS::LockDrawingSurface(PRInt32 aX, PRInt32 aY, PRUint32 aWidth,
00152        PRUint32 aHeight, void **aBits, PRInt32 *aStride, PRInt32 *aWidthBytes, PRUint32 aFlags)
00153 {
00154        PushState();
00155        return mSurface->Lock(aX, aY, aWidth, aHeight, aBits, aStride, aWidthBytes, aFlags);
00156 }
00157 
00158 NS_IMETHODIMP nsRenderingContextBeOS::UnlockDrawingSurface()
00159 {
00160        PopState();
00161        mSurface->Unlock();
00162        return NS_OK;
00163 }
00164 
00165 NS_IMETHODIMP nsRenderingContextBeOS::SelectOffScreenDrawingSurface(nsIDrawingSurface* aSurface)
00166 {
00167        if (nsnull == aSurface)
00168               mSurface = mOffscreenSurface;
00169        else
00170               mSurface = (nsDrawingSurfaceBeOS *)aSurface;
00171        return NS_OK;
00172 }
00173 
00174 NS_IMETHODIMP nsRenderingContextBeOS::GetDrawingSurface(nsIDrawingSurface* *aSurface)
00175 {
00176        *aSurface = mSurface;
00177        return NS_OK;
00178 }
00179 
00180 NS_IMETHODIMP nsRenderingContextBeOS::Reset()
00181 {
00182        return NS_OK;
00183 }
00184 
00185 NS_IMETHODIMP nsRenderingContextBeOS::GetDeviceContext(nsIDeviceContext *&aContext)
00186 {
00187        NS_IF_ADDREF(mContext);
00188        aContext = mContext;
00189        return NS_OK;
00190 }
00191 
00192 // Get a new GS
00193 NS_IMETHODIMP nsRenderingContextBeOS::PushState()
00194 {
00195 #ifdef USE_GS_POOL
00196        nsGraphicsState *state = nsGraphicsStatePool::GetNewGS();
00197 #else
00198        nsGraphicsState *state = new nsGraphicsState;
00199 #endif
00200        // Push into this state object, add to vector
00201        if (!state)
00202               return NS_ERROR_OUT_OF_MEMORY;
00203 
00204        nsTransform2D *tranMatrix;
00205        if (nsnull == mTranMatrix)
00206               tranMatrix = new nsTransform2D();
00207        else
00208               tranMatrix = new nsTransform2D(mTranMatrix);
00209 
00210        if (!tranMatrix)
00211        {
00212 #ifdef USE_GS_POOL
00213               nsGraphicsStatePool::ReleaseGS(state);
00214 #else
00215               delete state;
00216 #endif
00217               return NS_ERROR_OUT_OF_MEMORY;
00218        }
00219        state->mMatrix = mTranMatrix;
00220        mTranMatrix = tranMatrix;
00221 
00222        // Set state to mClipRegion. SetClip{Rect,Region}() will do copy-on-write stuff
00223        state->mClipRegion = mClipRegion;
00224 
00225        NS_IF_ADDREF(mFontMetrics);
00226        state->mFontMetrics = mFontMetrics;
00227        state->mColor = mCurrentColor;
00228        state->mLineStyle = mCurrentLineStyle;
00229 
00230        mStateCache->AppendElement(state);
00231        return NS_OK;
00232 }
00233 
00234 NS_IMETHODIMP nsRenderingContextBeOS::PopState(void)
00235 {
00236        PRUint32 cnt = mStateCache->Count();
00237        nsGraphicsState *state;
00238        
00239        if (cnt > 0)
00240        {
00241               state = (nsGraphicsState *)mStateCache->ElementAt(cnt - 1);
00242               mStateCache->RemoveElementAt(cnt - 1);
00243               
00244               // Assign all local attributes from the state object just popped
00245               if (state->mMatrix)
00246               {
00247                      delete mTranMatrix;
00248                      mTranMatrix = state->mMatrix;
00249               }
00250               
00251               mClipRegion = state->mClipRegion;
00252               if (state->mFontMetrics && (mFontMetrics != state->mFontMetrics))
00253                      SetFont(state->mFontMetrics);
00254               if (state->mColor != mCurrentColor)
00255                      SetColor(state->mColor);
00256               if (state->mLineStyle != mCurrentLineStyle)
00257                      SetLineStyle(state->mLineStyle);
00258 
00259               // Delete this graphics state object
00260 #ifdef USE_GS_POOL
00261               nsGraphicsStatePool::ReleaseGS(state);
00262 #else
00263               delete state;
00264 #endif
00265        }
00266        
00267        return NS_OK;
00268 }
00269 
00270 NS_IMETHODIMP nsRenderingContextBeOS::IsVisibleRect(const nsRect& aRect, PRBool &aVisible)
00271 {
00272        aVisible = PR_TRUE;
00273        return NS_OK;
00274 }
00275 
00276 NS_IMETHODIMP nsRenderingContextBeOS::GetClipRect(nsRect &aRect, PRBool &aClipValid)
00277 {
00278        if (!mClipRegion)
00279               return NS_ERROR_FAILURE;
00280        if (!mClipRegion->IsEmpty())
00281        {
00282               PRInt32 x, y, w, h;
00283               mClipRegion->GetBoundingBox(&x, &y, &w, &h);
00284               aRect.SetRect(x, y, w, h);
00285               aClipValid = PR_TRUE;
00286        }
00287        else
00288        {
00289               aRect.SetRect(0, 0, 0, 0);
00290               aClipValid = PR_FALSE;
00291        }
00292        return NS_OK;
00293 }
00294 
00295 NS_IMETHODIMP nsRenderingContextBeOS::SetClipRect(const nsRect& aRect, nsClipCombine aCombine)
00296 {
00297        
00298        PRUint32 cnt = mStateCache->Count();
00299        nsGraphicsState *state = nsnull;
00300        if (cnt > 0)
00301               state = (nsGraphicsState *)mStateCache->ElementAt(cnt - 1);
00302        
00303        if (state && mClipRegion && state->mClipRegion == mClipRegion)
00304        {
00305               nsCOMPtr<nsIRegion> region;
00306               GetClipRegion(getter_AddRefs(region));
00307               mClipRegion = region;
00308        }
00309        
00310        CreateClipRegion();
00311        nsRect trect = aRect;
00312        mTranMatrix->TransformCoord(&trect.x, &trect.y, &trect.width, &trect.height);
00313 
00314        switch (aCombine)
00315        {
00316               case nsClipCombine_kIntersect:
00317                      mClipRegion->Intersect(trect.x, trect.y, trect.width, trect.height);
00318                      break;
00319               case nsClipCombine_kUnion:
00320                      mClipRegion->Union(trect.x, trect.y, trect.width, trect.height);
00321                      break;
00322               case nsClipCombine_kSubtract:
00323                      mClipRegion->Subtract(trect.x, trect.y, trect.width, trect.height);
00324                      break;
00325               case nsClipCombine_kReplace:
00326                      mClipRegion->SetTo(trect.x, trect.y, trect.width, trect.height);
00327                      break;
00328        }
00329        return NS_OK;
00330 } 
00331 
00332 // To reduce locking overhead, the caller must unlock the looper itself.
00333 // TO DO: Locking and unlocking around each graphics primitive is still very lame
00334 // and imposes serious overhead. Now we fixed this problem for offscreen surfaces,
00335 // but there are also BViews "imported" from nsWidget
00336 bool nsRenderingContextBeOS::LockAndUpdateView() 
00337 {
00338        bool rv = false;
00339        if (!mSurface)
00340               return rv;
00341        if (mView) 
00342               mView = nsnull;
00343        mSurface->AcquireView(&mView);
00344        if (!mView)
00345               return rv; 
00346 
00347        // Intelligent lock
00348        if (mSurface->LockDrawable()) 
00349        {
00350               // if BFont wasn't set already
00351               if (mCurrentBFont == nsnull)
00352               { 
00353                      if (mFontMetrics)
00354                             mFontMetrics->GetFontHandle((nsFontHandle)mCurrentBFont);
00355 
00356                      if (mCurrentBFont)
00357                             mView->SetFont(mCurrentBFont);
00358                      else
00359                             mView->SetFont(be_plain_font); // fallback - only for single call
00360               }
00361 
00362               if (mClipRegion) 
00363               {
00364                      BRegion *region = nsnull;
00365                      mClipRegion->GetNativeRegion((void *&)region);
00366                      mView->ConstrainClippingRegion(region);
00367               }
00368               else
00369               {
00370                      mView->ConstrainClippingRegion(0);
00371               }
00372               rv = true;
00373        }
00374        return rv;
00375 }
00376 
00377 void nsRenderingContextBeOS::UnlockView()
00378 {
00379        mSurface->UnlockDrawable();
00380 }
00381        
00382 void nsRenderingContextBeOS::CreateClipRegion()
00383 {
00384        // We have 3 cases to deal with:
00385        //  1 - There is no mClipRegion -> Create one
00386        //  2 - There is an mClipRegion shared w/ stack -> Duplicate and unshare
00387        //  3 - There is an mClipRegion and its not shared -> return
00388 
00389        if (mClipRegion)
00390        {
00391               PRUint32 cnt = mStateCache->Count();
00392 
00393               if (cnt == 0) 
00394                      return;
00395 
00396               nsGraphicsState *state;
00397               state = (nsGraphicsState *)mStateCache->ElementAt(cnt - 1);
00398 
00399               if (state->mClipRegion != mClipRegion)
00400                      return;
00401                      
00402               mClipRegion = new nsRegionBeOS;
00403               if (mClipRegion)
00404                      mClipRegion->SetTo(*state->mClipRegion);
00405        }
00406        else
00407        {
00408               PRUint32 w, h;
00409               mSurface->GetSize(&w, &h);
00410 
00411               mClipRegion = new nsRegionBeOS;
00412               if (mClipRegion)
00413               {
00414                      mClipRegion->Init();
00415                      mClipRegion->SetTo(0, 0, w, h);
00416               }
00417        }
00418 }
00419        
00420 NS_IMETHODIMP nsRenderingContextBeOS::SetClipRegion(const nsIRegion &aRegion, nsClipCombine aCombine)
00421 {
00422        
00423        PRUint32 cnt = mStateCache->Count();
00424        nsGraphicsState *state = nsnull;
00425        if (cnt > 0)
00426               state = (nsGraphicsState *)mStateCache->ElementAt(cnt - 1);
00427        
00428        if (state && mClipRegion && state->mClipRegion == mClipRegion)
00429        {
00430               nsCOMPtr<nsIRegion> region;
00431               GetClipRegion(getter_AddRefs(region));
00432               mClipRegion = region;
00433        }
00434        
00435        CreateClipRegion();
00436        switch (aCombine)
00437        {
00438               case nsClipCombine_kIntersect:
00439                      mClipRegion->Intersect(aRegion);
00440                      break;
00441               case nsClipCombine_kUnion:
00442                      mClipRegion->Union(aRegion);
00443                      break;
00444               case nsClipCombine_kSubtract:
00445                      mClipRegion->Subtract(aRegion);
00446                      break;
00447               case nsClipCombine_kReplace:
00448                      mClipRegion->SetTo(aRegion);
00449                      break;
00450        }
00451        return NS_OK;
00452 }
00453 
00454 NS_IMETHODIMP nsRenderingContextBeOS::CopyClipRegion(nsIRegion &aRegion)
00455 {
00456        if (!mClipRegion)
00457               return NS_ERROR_FAILURE;
00458        aRegion.SetTo(*mClipRegion);
00459        return NS_OK;
00460 }
00461 
00462 NS_IMETHODIMP nsRenderingContextBeOS::GetClipRegion(nsIRegion **aRegion)
00463 {
00464        if (!aRegion || !mClipRegion)
00465               return NS_ERROR_NULL_POINTER;
00466        
00467        if (*aRegion) // copy it, they should be using CopyClipRegion
00468        {
00469               (*aRegion)->SetTo(*mClipRegion);
00470        }
00471        else
00472        {
00473               nsCOMPtr<nsIRegion> newRegion = new nsRegionBeOS();
00474               newRegion->Init();
00475               newRegion->SetTo(*mClipRegion);
00476               NS_ADDREF(*aRegion = newRegion);
00477        }
00478        return NS_OK;
00479 }
00480 
00481 NS_IMETHODIMP nsRenderingContextBeOS::SetColor(nscolor aColor) 
00482 {
00483        if (nsnull == mContext)
00484               return NS_ERROR_FAILURE;
00485        mCurrentColor = aColor;
00486        mRGB_color.red = NS_GET_R(mCurrentColor);
00487        mRGB_color.green = NS_GET_G(mCurrentColor);
00488        mRGB_color.blue = NS_GET_B(mCurrentColor);
00489        mRGB_color.alpha = 255;     
00490        if (LockAndUpdateView())
00491        {
00492               mView->SetHighColor(mRGB_color);
00493               UnlockView();
00494        }
00495        return NS_OK;
00496 }
00497 
00498 NS_IMETHODIMP nsRenderingContextBeOS::GetColor(nscolor &aColor) const 
00499 {
00500        aColor = mCurrentColor;
00501        return NS_OK;
00502 }
00503 
00504 NS_IMETHODIMP nsRenderingContextBeOS::SetFont(const nsFont &aFont, nsIAtom* aLangGroup)
00505 {
00506        nsCOMPtr<nsIFontMetrics> newMetrics;
00507        nsresult rv = mContext->GetMetricsFor(aFont, aLangGroup, *getter_AddRefs(newMetrics));
00508        if (NS_SUCCEEDED(rv)) 
00509               rv = SetFont(newMetrics);
00510        return rv;
00511 }
00512 
00513 NS_IMETHODIMP nsRenderingContextBeOS::SetFont(nsIFontMetrics *aFontMetrics)
00514 {
00515        mCurrentBFont = nsnull;
00516        NS_IF_RELEASE(mFontMetrics);
00517        mFontMetrics = aFontMetrics;
00518        NS_IF_ADDREF(mFontMetrics);
00519        // Assigning value to mCurrentBFont and setting it as mView font
00520        if (LockAndUpdateView())
00521               UnlockView();
00522        return NS_OK;
00523 }
00524 
00525 NS_IMETHODIMP nsRenderingContextBeOS::SetLineStyle(nsLineStyle aLineStyle)
00526 {
00527        switch(aLineStyle)
00528        {
00529               case nsLineStyle_kDashed:
00530                      mCurrentLinePattern = dashes;
00531                      break;
00532               case nsLineStyle_kDotted:
00533                      mCurrentLinePattern = dots;
00534                      break;
00535               case nsLineStyle_kSolid:
00536               default:
00537                      mCurrentLinePattern = B_SOLID_HIGH;
00538               break;
00539        }
00540        mCurrentLineStyle = aLineStyle ;
00541        return NS_OK;
00542 }
00543 
00544 NS_IMETHODIMP nsRenderingContextBeOS::GetLineStyle(nsLineStyle &aLineStyle)
00545 {
00546        aLineStyle = mCurrentLineStyle;
00547        return NS_OK;
00548 }
00549 
00550 NS_IMETHODIMP nsRenderingContextBeOS::GetFontMetrics(nsIFontMetrics *&aFontMetrics)
00551 {
00552        NS_IF_ADDREF(mFontMetrics);
00553        aFontMetrics = mFontMetrics;
00554        return NS_OK;
00555 }
00556 
00557 // Add the passed in translation to the current translation
00558 NS_IMETHODIMP nsRenderingContextBeOS::Translate(nscoord aX, nscoord aY)
00559 {
00560        mTranMatrix->AddTranslation((float)aX, (float)aY);
00561        return NS_OK;
00562 }
00563 
00564 // Add the passed in scale to the current scale
00565 NS_IMETHODIMP nsRenderingContextBeOS::Scale(float aSx, float aSy)
00566 {
00567        mTranMatrix->AddScale(aSx, aSy);
00568        return NS_OK;
00569 }
00570 
00571 NS_IMETHODIMP nsRenderingContextBeOS::GetCurrentTransform(nsTransform2D *&aTransform)
00572 {
00573        aTransform = mTranMatrix;
00574        return NS_OK;
00575 }
00576 
00577 NS_IMETHODIMP nsRenderingContextBeOS::CreateDrawingSurface(const nsRect& aBounds, PRUint32 aSurfFlags,
00578        nsIDrawingSurface* &aSurface)
00579 {
00580        
00581        if (nsnull == mSurface)
00582        {
00583               aSurface = nsnull;
00584               return NS_ERROR_FAILURE;
00585        }
00586        
00587        if ((aBounds.width <= 0) || (aBounds.height <= 0))
00588               return NS_ERROR_FAILURE;
00589        
00590        nsDrawingSurfaceBeOS *surf = new nsDrawingSurfaceBeOS();
00591        if (!surf)
00592        {
00593               aSurface = nsnull;
00594               return NS_ERROR_FAILURE;
00595        }
00596 
00597        NS_ADDREF(surf);
00598        // Getting drawable and updating context
00599        if (LockAndUpdateView())
00600               UnlockView();
00601               
00602        surf->Init(mView, aBounds.width, aBounds.height, aSurfFlags);
00603        aSurface = surf;
00604        return NS_OK;
00605 }
00606 
00607 NS_IMETHODIMP nsRenderingContextBeOS::DestroyDrawingSurface(nsIDrawingSurface* aDS)
00608 {
00609        nsDrawingSurfaceBeOS *surf = (nsDrawingSurfaceBeOS *)aDS;
00610        if (surf == nsnull)
00611               return NS_ERROR_FAILURE;
00612        NS_IF_RELEASE(surf);
00613        return NS_OK;
00614 }
00615 
00616 NS_IMETHODIMP nsRenderingContextBeOS::DrawLine(nscoord aX0, nscoord aY0, nscoord aX1, nscoord aY1) {
00617        if (mTranMatrix == nsnull)
00618               return NS_ERROR_FAILURE;
00619        if (mSurface == nsnull)
00620               return NS_ERROR_FAILURE;
00621        
00622        mTranMatrix->TransformCoord(&aX0, &aY0);
00623        mTranMatrix->TransformCoord(&aX1, &aY1);
00624        nscoord diffX = aX1 - aX0;
00625        nscoord diffY = aY1 - aY0;
00626        
00627        if (0 != diffX)
00628               diffX = (diffX > 0) ? 1 : -1;
00629        if (0 != diffY)
00630               diffY = (diffY > 0) ? 1 : -1;
00631        
00632        
00633        if (LockAndUpdateView())
00634        {
00635               mView->StrokeLine(BPoint(aX0, aY0), BPoint(aX1 - diffX, aY1 - diffY), mCurrentLinePattern);
00636               UnlockView();
00637        }
00638        return NS_OK;
00639 }
00640 
00641 NS_IMETHODIMP nsRenderingContextBeOS::DrawPolyline(const nsPoint aPoints[], PRInt32 aNumPoints)
00642 {
00643        if (mTranMatrix == nsnull)
00644               return NS_ERROR_FAILURE;
00645        if (mSurface == nsnull)
00646               return NS_ERROR_FAILURE;
00647        BPoint *pts;
00648        BPolygon poly;
00649        BRect r;
00650        PRInt32 w, h;
00651        //allocating from stack if amount isn't big
00652        BPoint bpointbuf[64];
00653        pts = bpointbuf;
00654        if (aNumPoints>64)
00655               pts = new BPoint[aNumPoints];
00656        for (int i = 0; i < aNumPoints; ++i)
00657        {
00658               nsPoint p = aPoints[i];
00659               mTranMatrix->TransformCoord(&p.x, &p.y);
00660               pts[i].x = p.x;
00661               pts[i].y = p.y;
00662 #ifdef DEBUG
00663               printf("polyline(%i,%i)\n", p.x, p.y);
00664 #endif
00665        }
00666        poly.AddPoints(pts, aNumPoints);
00667        r = poly.Frame();    
00668        w = r.IntegerWidth();
00669        h = r.IntegerHeight();
00670 //     Don't draw empty polygon
00671        if (w && h)
00672        {
00673               if (LockAndUpdateView())
00674               {
00675                      if (1 == h) 
00676                      {
00677                             mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left + w - 1, r.top), mCurrentLinePattern);
00678                      }
00679                      else if (1 == w)
00680                      {
00681                             mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left, r.top + h -1), mCurrentLinePattern);
00682                      }
00683                      else
00684                      {
00685                             poly.MapTo(r,BRect(r.left, r.top, r.left + w -1, r.top + h - 1));
00686                             mView->StrokePolygon(&poly, false, mCurrentLinePattern);
00687                      }
00688                      UnlockView();
00689               }             
00690        }      
00691        if (pts!=bpointbuf)
00692               delete [] pts;
00693        return NS_OK;
00694 }
00695 
00696 NS_IMETHODIMP nsRenderingContextBeOS::DrawRect(const nsRect& aRect)
00697 {
00698        return DrawRect(aRect.x, aRect.y, aRect.width, aRect.height);
00699 }
00700 
00701 NS_IMETHODIMP nsRenderingContextBeOS::DrawRect(nscoord aX, nscoord aY, nscoord aWidth,
00702        nscoord aHeight)
00703 {
00704        
00705        if (nsnull == mTranMatrix || nsnull == mSurface)
00706               return NS_ERROR_FAILURE;
00707 
00708        // After the transform, if the numbers are huge, chop them, because
00709        // they're going to be converted from 32 bit to 16 bit.
00710        // It's all way off the screen anyway.
00711        nscoord x = aX, y = aY, w = aWidth, h = aHeight;
00712        mTranMatrix->TransformCoord(&x, &y, &w, &h);
00713        ConditionRect(x, y, w, h);
00714        
00715        // Don't draw empty rectangles; also, w/h are adjusted down by one
00716        // so that the right number of pixels are drawn.
00717        if (w && h)
00718        {
00719               if (LockAndUpdateView())
00720               {
00721                      if (1 == h)
00722                             mView->StrokeLine(BPoint(x, y), BPoint(x + w - 1, y), mCurrentLinePattern);
00723                      else
00724                             mView->StrokeRect(BRect(x, y, x + w - 1, y + h - 1), mCurrentLinePattern);
00725                      UnlockView();
00726               }
00727        }
00728        return NS_OK;
00729 }
00730 
00731 NS_IMETHODIMP nsRenderingContextBeOS::FillRect(const nsRect &aRect)
00732 {
00733        return FillRect(aRect.x, aRect.y, aRect.width, aRect.height);
00734 }
00735 
00736 NS_IMETHODIMP nsRenderingContextBeOS::FillRect(nscoord aX, nscoord aY, nscoord aWidth,
00737        nscoord aHeight)
00738 {
00739        
00740        if (nsnull == mTranMatrix || nsnull == mSurface)
00741               return NS_ERROR_FAILURE;
00742        
00743        // After the transform, if the numbers are huge, chop them, because
00744        // they're going to be converted from 32 bit to 16 bit.
00745        // It's all way off the screen anyway.
00746        nscoord x = aX, y = aY, w = aWidth, h = aHeight;
00747        mTranMatrix->TransformCoord(&x, &y, &w, &h);
00748        ConditionRect(x, y, w, h);
00749        
00750        if (w && h)
00751        {
00752               if (LockAndUpdateView())
00753               {
00754                      if (1 == h)
00755                             mView->StrokeLine(BPoint(x, y), BPoint(x + w - 1, y), mCurrentLinePattern);
00756                      else
00757                             mView->FillRect(BRect(x, y, x + w - 1, y + h - 1), B_SOLID_HIGH);
00758                      UnlockView();
00759               }
00760        }
00761        return NS_OK;
00762 }
00763 
00764 NS_IMETHODIMP nsRenderingContextBeOS::InvertRect(const nsRect &aRect)
00765 {
00766        return InvertRect(aRect.x, aRect.y, aRect.width, aRect.height);
00767 }
00768 
00769 NS_IMETHODIMP nsRenderingContextBeOS::InvertRect(nscoord aX, nscoord aY, nscoord aWidth,
00770        nscoord aHeight)
00771 {
00772        
00773        if (nsnull == mTranMatrix || nsnull == mSurface)
00774               return NS_ERROR_FAILURE;
00775        
00776        // After the transform, if the numbers are huge, chop them, because
00777        // they're going to be converted from 32 bit to 16 bit.
00778        // It's all way off the screen anyway.
00779        nscoord x = aX, y = aY, w = aWidth, h = aHeight;
00780        mTranMatrix->TransformCoord(&x, &y, &w, &h);
00781        ConditionRect(x, y, w, h);
00782        
00783        if (w && h)
00784        {
00785               if (LockAndUpdateView())
00786               {
00787                      //Mozilla doesn't seem to set clipping for InvertRect, so we do it here - bug 230267
00788                      BRegion tmpClip = BRegion(BRect(x, y, x + w - 1, y + h - 1));
00789                      mView->ConstrainClippingRegion(&tmpClip);
00790                      mView->InvertRect(BRect(x, y, x + w - 1, y + h - 1));
00791                      mView->Sync();
00792                      UnlockView();
00793               }
00794        }
00795        return NS_OK;
00796 }
00797 
00798 NS_IMETHODIMP nsRenderingContextBeOS::DrawPolygon(const nsPoint aPoints[], PRInt32 aNumPoints)
00799 {
00800        if (nsnull == mTranMatrix || nsnull == mSurface)
00801               return NS_ERROR_FAILURE;
00802        BPoint *pts;
00803        BPolygon poly;
00804        BRect r;
00805        PRInt32 w, h;
00806        //allocating from stack if amount isn't big
00807        BPoint bpointbuf[64];
00808        pts = bpointbuf;
00809        if (aNumPoints>64)
00810               pts = new BPoint[aNumPoints];
00811        for (int i = 0; i < aNumPoints; ++i)
00812        {
00813               nsPoint p = aPoints[i];
00814               mTranMatrix->TransformCoord(&p.x, &p.y);
00815               pts[i].x = p.x;
00816               pts[i].y = p.y;
00817        }
00818        poly.AddPoints(pts, aNumPoints);
00819        r = poly.Frame();    
00820        w = r.IntegerWidth();
00821        h = r.IntegerHeight();
00822 //     Don't draw empty polygon
00823        if (w && h)
00824        {
00825               if (LockAndUpdateView())
00826               {
00827                      if (1 == h)
00828                      {
00829                             mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left + w - 1, r.top), mCurrentLinePattern);
00830                      }
00831                      else if (1 == w)
00832                      {
00833                             mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left, r.top + h -1), mCurrentLinePattern);
00834                      }
00835                      else
00836                      {
00837                             poly.MapTo(r,BRect(r.left, r.top, r.left + w -1, r.top + h - 1));
00838                             mView->StrokePolygon(&poly, true, mCurrentLinePattern);
00839                      }
00840                      UnlockView();
00841               }             
00842        }             
00843        if (pts!=bpointbuf)
00844               delete [] pts;
00845        return NS_OK;
00846 }
00847 
00848 NS_IMETHODIMP nsRenderingContextBeOS::FillPolygon(const nsPoint aPoints[], PRInt32 aNumPoints)
00849 {
00850        if (nsnull == mTranMatrix || nsnull == mSurface)
00851               return NS_ERROR_FAILURE;
00852        
00853        BPoint *pts;
00854        BPolygon poly;
00855        BRect r;
00856        PRInt32 w, h;
00857        BPoint bpointbuf[64];
00858        pts = bpointbuf;
00859        if (aNumPoints>64)
00860               pts = new BPoint[aNumPoints];
00861        for (int i = 0; i < aNumPoints; ++i)
00862        {
00863               nsPoint p = aPoints[i];
00864               mTranMatrix->TransformCoord(&p.x, &p.y);
00865               pts[i].x = p.x;
00866               pts[i].y = p.y;
00867        }
00868        poly.AddPoints(pts, aNumPoints);
00869        r = poly.Frame();
00870        w = r.IntegerWidth();
00871        h = r.IntegerHeight();
00872 //     Don't draw empty polygon
00873        if (w && h)
00874        {
00875               if (LockAndUpdateView())
00876               {
00877                      if (1 == h)
00878                      {
00879                             mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left + w - 1, r.top), mCurrentLinePattern);
00880                      }
00881                      else if (1 == w)
00882                      {
00883                             mView->StrokeLine(BPoint(r.left, r.top), BPoint(r.left, r.top + h -1), mCurrentLinePattern);
00884                      }
00885                      else
00886                      {
00887                             poly.MapTo(r,BRect(r.left, r.top, r.left + w -1, r.top + h - 1));
00888                             mView->FillPolygon(&poly, B_SOLID_HIGH);
00889                      }
00890                      UnlockView();
00891               }             
00892        }
00893        if (pts!=bpointbuf)
00894               delete [] pts;
00895        return NS_OK;
00896 }
00897 
00898 NS_IMETHODIMP nsRenderingContextBeOS::DrawEllipse(const nsRect &aRect)
00899 {
00900        return DrawEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
00901 }
00902 
00903 NS_IMETHODIMP nsRenderingContextBeOS::DrawEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00904 {
00905        if (nsnull == mTranMatrix || nsnull == mSurface)
00906               return NS_ERROR_FAILURE;
00907 
00908        nscoord x = aX, y = aY, w = aWidth, h = aHeight;
00909        mTranMatrix->TransformCoord(&x, &y, &w, &h);
00910        
00911        if (LockAndUpdateView())
00912        {
00913               mView->StrokeEllipse(BRect(x, y, x + w - 1, y + h - 1), mCurrentLinePattern);
00914               UnlockView();
00915        }
00916        return NS_OK;
00917 }
00918 
00919 NS_IMETHODIMP nsRenderingContextBeOS::FillEllipse(const nsRect &aRect)
00920 {
00921        return FillEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
00922 }
00923 
00924 NS_IMETHODIMP nsRenderingContextBeOS::FillEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00925 {
00926        if (nsnull == mTranMatrix || nsnull == mSurface)
00927               return NS_ERROR_FAILURE;
00928 
00929        nscoord x = aX, y = aY, w = aWidth, h = aHeight;
00930        mTranMatrix->TransformCoord(&x, &y, &w, &h);
00931        
00932        if (LockAndUpdateView())
00933        {
00934               mView->FillEllipse(BRect(x, y, x + w - 1, y + h - 1));
00935               UnlockView();
00936        }
00937        return NS_OK;
00938 }
00939 
00940 NS_IMETHODIMP nsRenderingContextBeOS::DrawArc(const nsRect& aRect, float aStartAngle, float aEndAngle)
00941 {
00942        return DrawArc(aRect.x, aRect.y, aRect.width, aRect.height, aStartAngle, aEndAngle);
00943 }
00944 
00945 NS_IMETHODIMP nsRenderingContextBeOS::DrawArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
00946        float aStartAngle, float aEndAngle)
00947 {
00948        if (nsnull == mTranMatrix || nsnull == mSurface)
00949               return NS_ERROR_FAILURE;
00950 
00951        nscoord x = aX, y = aY, w = aWidth, h = aHeight;
00952        mTranMatrix->TransformCoord(&x, &y, &w, &h);
00953        
00954        if (LockAndUpdateView())
00955        {
00956               mView->StrokeArc(BRect(x, y, x + w - 1, y + h - 1), aStartAngle, aEndAngle - aStartAngle, mCurrentLinePattern);
00957               UnlockView();
00958        }
00959        return NS_OK;
00960 }
00961 
00962 NS_IMETHODIMP nsRenderingContextBeOS::FillArc(const nsRect &aRect, float aStartAngle, float aEndAngle)
00963 {
00964        return FillArc(aRect.x, aRect.y, aRect.width, aRect.height, aStartAngle, aEndAngle);
00965 }
00966 
00967 NS_IMETHODIMP nsRenderingContextBeOS::FillArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
00968        float aStartAngle, float aEndAngle)
00969 {
00970        if (nsnull == mTranMatrix || nsnull == mSurface)
00971               return NS_ERROR_FAILURE;
00972 
00973        nscoord x = aX, y = aY, w = aWidth, h = aHeight;
00974        mTranMatrix->TransformCoord(&x, &y, &w, &h);
00975        
00976        if (LockAndUpdateView())
00977        {
00978               mView->FillArc(BRect(x, y, x + w - 1, y + h - 1), aStartAngle, aEndAngle - aStartAngle);
00979               UnlockView();
00980        }
00981        return NS_OK;
00982 }
00983 
00984 // Block of UTF-8 helpers
00985 // Whole block of utf-8 helpers must  be placed before any gfx text method
00986 // in order to allow text handling directly from PRUnichar* methods in future
00987 
00988 // From BeNewsLetter #82:
00989 // Get the number of character bytes for a given utf8 byte character
00990 inline uint32 utf8_char_len(uchar byte) 
00991 {
00992        return (((0xE5000000 >> ((byte >> 3) & 0x1E)) & 3) + 1);
00993 }
00994 
00995 // useful UTF-8 utilities 
00996 
00997 #define BEGINS_CHAR(byte) ((byte & 0xc0) != 0x80)
00998 
00999 inline uint32 utf8_str_len(const char* ustring) 
01000 {
01001        uint32 cnt = 0;
01002        while ( *ustring != '\0')
01003        {
01004               if ( BEGINS_CHAR( *ustring ) )
01005                      ++cnt;
01006                      ++ustring;
01007        }
01008        return cnt;       
01009 }
01010 
01011 // Macro to convert a ushort* uni_string into a char* or uchar* utf8_string,
01012 // one character at a time. Move the pointer. You can check terminaison on
01013 // the uni_string by testing : if (uni_string[0] == 0)
01014 // WARNING : you need to use EXPLICIT POINTERS for both str and unistr.
01015 #define convert_to_utf8(str, uni_str) {\
01016        if ((uni_str[0]&0xff80) == 0)\
01017               *str++ = *uni_str++;\
01018        else if ((uni_str[0]&0xf800) == 0) {\
01019               str[0] = 0xc0|(uni_str[0]>>6);\
01020               str[1] = 0x80|((*uni_str++)&0x3f);\
01021               str += 2;\
01022        } else if ((uni_str[0]&0xfc00) != 0xd800) {\
01023               str[0] = 0xe0|(uni_str[0]>>12);\
01024               str[1] = 0x80|((uni_str[0]>>6)&0x3f);\
01025               str[2] = 0x80|((*uni_str++)&0x3f);\
01026               str += 3;\
01027        } else {\
01028               int val;\
01029               val = ((uni_str[0]-0xd7c0)<<10) | (uni_str[1]&0x3ff);\
01030               str[0] = 0xf0 | (val>>18);\
01031               str[1] = 0x80 | ((val>>12)&0x3f);\
01032               str[2] = 0x80 | ((val>>6)&0x3f);\
01033               str[3] = 0x80 | (val&0x3f);\
01034               uni_str += 2; str += 4;\
01035        }\
01036 }
01037 
01038 // End of block of UTF-8 helpers
01039 
01040 NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(char aC, nscoord &aWidth)
01041 {
01042        return GetWidth(&aC, 1, aWidth);
01043 }
01044 
01045 NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(PRUnichar aC, nscoord &aWidth, PRInt32 *aFontID)
01046 {
01047        return GetWidth(&aC, 1, aWidth, aFontID);
01048 }
01049 
01050 NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const nsString &aString, nscoord& aWidth, PRInt32 *aFontID)
01051 {
01052        return GetWidth(aString.get(), aString.Length(), aWidth, aFontID);
01053 }
01054 
01055 NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const char *aString, nscoord &aWidth)
01056 {
01057        return GetWidth(aString, strlen(aString), aWidth);
01058 }
01059 
01060 NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const char *aString, PRUint32 aLength, nscoord &aWidth)
01061 {
01062        if (0 == aLength)
01063        {
01064               aWidth = 0;
01065        }
01066        else
01067        {
01068               if (aString == nsnull)
01069                      return NS_ERROR_FAILURE;
01070               // Using cached width if possible
01071               aWidth  = nscoord(((nsFontMetricsBeOS *)mFontMetrics)->GetStringWidth((char *)aString, aLength) * mP2T);
01072        }
01073        return NS_OK;
01074 }
01075 
01076 
01077 NS_IMETHODIMP nsRenderingContextBeOS::GetWidth(const PRUnichar *aString, PRUint32 aLength,
01078        nscoord &aWidth, PRInt32 *aFontID)
01079 {
01080        // max UTF-8 string length
01081        uint8 *utf8str = new uint8[aLength * 4 + 1];
01082        uint8 *utf8ptr = utf8str;
01083        const PRUnichar *uniptr = aString;
01084        
01085        for (PRUint32 i = 0; i < aLength; i++)
01086        {
01087               convert_to_utf8(utf8ptr, uniptr);
01088        }
01089        
01090        *utf8ptr = '\0';
01091        uint32 utf8str_len = strlen((char *)utf8str);
01092        GetWidth((char *)utf8str, utf8str_len, aWidth);
01093        delete [] utf8str;
01094        return NS_OK;
01095 }
01096 
01097 NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const char *aString, PRUint32 aLength,
01098        nsTextDimensions &aDimensions)
01099 {
01100        if (mFontMetrics)
01101        {
01102               mFontMetrics->GetMaxAscent(aDimensions.ascent);
01103               mFontMetrics->GetMaxDescent(aDimensions.descent);
01104        }      
01105        return GetWidth(aString, aLength, aDimensions.width);
01106 }
01107 
01108 NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const PRUnichar *aString, PRUint32 aLength,
01109        nsTextDimensions &aDimensions, PRInt32 *aFontID)
01110 {
01111        if (mFontMetrics)
01112        {
01113               mFontMetrics->GetMaxAscent(aDimensions.ascent);
01114               mFontMetrics->GetMaxDescent(aDimensions.descent);
01115        }      
01116        return GetWidth(aString, aLength, aDimensions.width, aFontID);
01117 }
01118 
01119 // FAST TEXT MEASURE methods
01120 // Implementation is simplicistic in comparison with other platforms - we follow in this method
01121 // generic BeOS-port approach for string methods in GFX - convert from PRUnichar* to (UTF-8) char*
01122 // and call (UTF-8) char* version of the method.
01123 // It works well with current nsFontMetricsBeOS which is, again, simpler in comparison with
01124 // other platforms, partly due to UTF-8 nature of BeOS, partly due unimplemented font fallbacks
01125 // and other tricks.
01126 
01127 NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const PRUnichar* aString,
01128        PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, PRInt32 aNumBreaks,
01129        nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions,
01130        PRInt32* aFontID = nsnull)
01131 {
01132        nsresult ret_code = NS_ERROR_FAILURE;     
01133        uint8 utf8buf[1024];
01134        uint8* utf8str = nsnull;
01135        // max UTF-8 string length
01136        PRUint32 slength = aLength * 4 + 1;
01137        // Allocating char* array rather from stack than from heap for speed.
01138        //  1024 char array forms  e.g.  256 == 32*8 frame for CJK glyphs, which may be
01139        // insufficient for purpose of this method, but until we implement storage
01140        //in nsSurface, i think it is good compromise.
01141        if (slength < 1024) 
01142               utf8str = utf8buf;
01143        else 
01144               utf8str = new uint8[slength];
01145 
01146        uint8 *utf8ptr = utf8str;
01147        const PRUnichar *uniptr = aString;
01148        
01149        for (PRUint32 i = 0; i < aLength; ++i) 
01150               convert_to_utf8(utf8ptr, uniptr);
01151        
01152        *utf8ptr = '\0';
01153        ret_code = GetTextDimensions((char *)utf8str, utf8ptr-utf8str, aAvailWidth, aBreaks, aNumBreaks,
01154                                aDimensions, aNumCharsFit, aLastWordDimensions, aFontID);
01155        // deallocating if got from heap
01156        if (utf8str != utf8buf)
01157                      delete [] utf8str;
01158        return ret_code;
01159 }
01160 
01161 NS_IMETHODIMP nsRenderingContextBeOS::GetTextDimensions(const char* aString, PRInt32 aLength,
01162        PRInt32 aAvailWidth,PRInt32* aBreaks, PRInt32 aNumBreaks, nsTextDimensions& aDimensions,
01163        PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, PRInt32* aFontID = nsnull)
01164 {
01165        // Code is borrowed from win32 implementation including comments.
01166        // Minor changes are introduced due multibyte/utf-8 nature of char* strings handling in BeOS.
01167        char * utf8ptr = (char *)aString;
01168        PRInt32 *utf8pos =0; 
01169        PRInt32 num_of_glyphs = 0; // Number of glyphs isn't equal to number of bytes in case of UTF-8
01170        PRInt32 utf8posbuf[1025];
01171        if (aLength < 1025) 
01172               utf8pos = utf8posbuf;
01173        else 
01174               utf8pos = new PRInt32[aLength + 1];
01175 
01176        // counting number of glyphs (not bytes) in utf-8 string 
01177        //and recording positions of first byte for each utf-8 char
01178        PRInt32 i = 0;
01179        while (i < aLength)
01180        {
01181               if ( BEGINS_CHAR( utf8ptr[i] ) )
01182               {
01183                      utf8pos[num_of_glyphs] = i;
01184                      ++num_of_glyphs;
01185               }
01186               i++;
01187        }      
01188        utf8pos[num_of_glyphs] = i; // IMPORTANT for non-ascii strings for proper last-string-in-block.
01189        NS_PRECONDITION(aBreaks[aNumBreaks - 1] == num_of_glyphs, "invalid break array");
01190        
01191        // If we need to back up this state represents the last place we could
01192        // break. We can use this to avoid remeasuring text
01193        PRInt32 prevBreakState_BreakIndex = -1; // not known (hasn't been computed)
01194        nscoord prevBreakState_Width = 0; // accumulated width to this point
01195        mFontMetrics->GetMaxAscent(aLastWordDimensions.ascent);
01196        mFontMetrics->GetMaxDescent(aLastWordDimensions.descent);
01197        aLastWordDimensions.width = -1;
01198        aNumCharsFit = 0;
01199        // Iterate each character in the string and determine which font to use
01200        nscoord width = 0;
01201        PRInt32 start = 0;
01202        nscoord aveCharWidth;
01203        PRInt32 numBytes = 0;
01204        // allocating  array for positions of first bytes of utf-8 chars in utf-8 string
01205        // from stack if possible
01206        
01207        mFontMetrics->GetAveCharWidth(aveCharWidth);
01208 
01209 
01210        while (start < num_of_glyphs) 
01211        {
01212               // Estimate how many characters will fit. Do that by diving the available
01213               // space by the average character width. Make sure the estimated number
01214               // of characters is at least 1
01215               PRInt32 estimatedNumChars = 0;
01216               if (aveCharWidth > 0) 
01217                      estimatedNumChars = (aAvailWidth - width) / aveCharWidth;
01218 
01219               if (estimatedNumChars < 1) 
01220                      estimatedNumChars = 1;
01221 
01222               // Find the nearest break offset
01223               PRInt32 estimatedBreakOffset = start + estimatedNumChars;
01224               PRInt32 breakIndex;
01225               nscoord numChars;
01226               if (num_of_glyphs <= estimatedBreakOffset) 
01227               {
01228                      // All the characters should fit
01229                      numChars = num_of_glyphs - start;
01230                      // BeOS specifics - getting number of bytes from position array. Same for all remaining numBytes occurencies.
01231                      numBytes = aLength - utf8pos[start];
01232                      breakIndex = aNumBreaks - 1;
01233               }
01234               else 
01235               {
01236                      breakIndex = prevBreakState_BreakIndex;
01237                      while (((breakIndex + 1) < aNumBreaks) 
01238                                    && (aBreaks[breakIndex + 1] <= estimatedBreakOffset)) 
01239                      {
01240                             ++breakIndex;
01241                      }
01242                      
01243                      if (breakIndex == prevBreakState_BreakIndex) 
01244                             ++breakIndex; // make sure we advanced past the previous break index
01245 
01246                      numChars = aBreaks[breakIndex] - start;
01247                      numBytes = utf8pos[aBreaks[breakIndex]] - utf8pos[start];
01248               }
01249               nscoord twWidth = 0;
01250               if ((1 == numChars) && (aString[utf8pos[start]] == ' ')) 
01251               {
01252                      mFontMetrics->GetSpaceWidth(twWidth);
01253               } 
01254               else if (numChars > 0) 
01255               {
01256                      GetWidth(&aString[utf8pos[start]], numBytes, twWidth);
01257               } 
01258 
01259               // See if the text fits
01260               PRBool  textFits = (twWidth + width) <= aAvailWidth;
01261               // If the text fits then update the width and the number of
01262               // characters that fit
01263               if (textFits) 
01264               {
01265                      aNumCharsFit += numChars;
01266                      width += twWidth;
01267                      start += numChars;
01268 
01269                      // This is a good spot to back up to if we need to so remember
01270                      // this state
01271                      prevBreakState_BreakIndex = breakIndex;
01272                      prevBreakState_Width = width;
01273               }
01274               else 
01275               {
01276                      // See if we can just back up to the previous saved state and not
01277                      // have to measure any text
01278                      if (prevBreakState_BreakIndex > 0) 
01279                      {
01280                             // If the previous break index is just before the current break index
01281                             // then we can use it
01282                             if (prevBreakState_BreakIndex == (breakIndex - 1)) 
01283                             {
01284                                    aNumCharsFit = aBreaks[prevBreakState_BreakIndex];
01285                                    width = prevBreakState_Width;
01286                                    break;
01287                             }
01288                      }
01289                      // We can't just revert to the previous break state
01290                      if (0 == breakIndex)
01291                      {
01292                             // There's no place to back up to, so even though the text doesn't fit
01293                             // return it anyway
01294                             aNumCharsFit += numChars;
01295                             width += twWidth;
01296                             break;
01297                      }       
01298                      // Repeatedly back up until we get to where the text fits or we're all
01299                      // the way back to the first word
01300                      width += twWidth;
01301                      while ((breakIndex >= 1) && (width > aAvailWidth)) 
01302                      {
01303                             twWidth = 0;
01304                             start = aBreaks[breakIndex - 1];
01305                             numChars = aBreaks[breakIndex] - start;
01306                             numBytes = utf8pos[aBreaks[breakIndex]] - utf8pos[start];
01307                             if ((1 == numChars) && (aString[utf8pos[start]] == ' ')) 
01308                             {
01309                                    mFontMetrics->GetSpaceWidth(twWidth);
01310                             }
01311                             else if (numChars > 0) 
01312                             {
01313                                    GetWidth(&aString[utf8pos[start]], numBytes, twWidth);
01314                             }
01315                             width -= twWidth;
01316                             aNumCharsFit = start;
01317                             --breakIndex;
01318                      }
01319               break;   
01320               }             
01321        }
01322        aDimensions.width = width;
01323        mFontMetrics->GetMaxAscent(aDimensions.ascent);
01324        mFontMetrics->GetMaxDescent(aDimensions.descent);
01325        // deallocating if got from heap
01326        if(utf8pos != utf8posbuf) 
01327               delete utf8pos;
01328        return NS_OK;
01329 }
01330 
01331 NS_IMETHODIMP nsRenderingContextBeOS::DrawString(const nsString &aString,
01332        nscoord aX, nscoord aY, PRInt32 aFontID, const nscoord *aSpacing)
01333 {
01334        return DrawString(aString.get(), aString.Length(), aX, aY, aFontID, aSpacing);
01335 }
01336 
01337 
01338 // TO DO: A better solution is needed for both antialiasing as noted below and
01339 // character spacing - these are both suboptimal.
01340 NS_IMETHODIMP nsRenderingContextBeOS::DrawString(const char *aString, PRUint32 aLength,
01341        nscoord aX, nscoord aY, const nscoord *aSpacing)
01342 {
01343        
01344        if (0 == aLength)
01345               return NS_OK; 
01346 
01347        if (mTranMatrix == nsnull)
01348               return NS_ERROR_FAILURE;
01349        if (mSurface == nsnull)
01350               return NS_ERROR_FAILURE;
01351        if (aString == nsnull)
01352               return NS_ERROR_FAILURE;
01353 
01354        nscoord xx = aX, yy = aY, y=aY;
01355        
01356        if (LockAndUpdateView())  
01357        {
01358               PRBool doEmulateBold = PR_FALSE;
01359               
01360               if (mFontMetrics) 
01361               {
01362                      doEmulateBold = ((nsFontMetricsBeOS *)mFontMetrics)->IsBold() && !(mCurrentBFont->Face() & B_BOLD_FACE);
01363               }
01364               // XXX: B_OP_OVER isn't  most efficient for text rendering,
01365               // but it's the only way to render antialiased text correctly on arbitrary background
01366               PRBool offscreen;
01367               mSurface->IsOffscreen(&offscreen);
01368               mView->SetDrawingMode( offscreen ? B_OP_OVER : B_OP_COPY);
01369               if (nsnull == aSpacing || utf8_char_len((uchar)aString[0])==aLength) 
01370               {
01371                      mTranMatrix->TransformCoord(&xx, &yy);
01372                      mView->DrawString(aString, aLength, BPoint(xx, yy));
01373                      if (doEmulateBold)
01374                             mView->DrawString(aString, aLength, BPoint(xx + 1.0, yy));
01375               }
01376               else 
01377               {
01378                      char *wpoint =0;
01379                      int32 unichnum=0,  position=aX, ch_len=0;
01380                      for (PRUint32 i =0; i <= aLength; i += ch_len)
01381                      {
01382                             ch_len = utf8_char_len((uchar)aString[i]);
01383                             wpoint = (char *)(aString + i);
01384                             xx = position; 
01385                             yy = y;
01386                             mTranMatrix->TransformCoord(&xx, &yy);
01387                             // yy++; DrawString quirk!
01388                             mView->DrawString((char *)(wpoint), ch_len, BPoint(xx, yy));
01389                             if (doEmulateBold)
01390                                    mView->DrawString((char *)(wpoint), ch_len, BPoint(xx + 1.0, yy));
01391                             position += aSpacing[unichnum++];
01392                      }
01393               }
01394               mView->SetDrawingMode(B_OP_COPY);
01395               UnlockView();
01396        }
01397        return NS_OK;
01398 }
01399 
01400 NS_IMETHODIMP nsRenderingContextBeOS::DrawString(const PRUnichar *aString, PRUint32 aLength,
01401        nscoord aX, nscoord aY, PRInt32 aFontID, const nscoord *aSpacing)
01402 {
01403        
01404        // max UTF-8 string length
01405        uint8 *utf8str = new uint8[aLength * 4 + 1];
01406        uint8 *utf8ptr = utf8str;
01407        const PRUnichar *uniptr = aString;
01408        
01409        for (PRUint32 i = 0; i < aLength; i++) 
01410               convert_to_utf8(utf8ptr, uniptr);
01411        
01412        *utf8ptr = '\0';
01413        uint32 utf8str_len = strlen((char *)utf8str);
01414        DrawString((char *)utf8str, utf8str_len, aX, aY, aSpacing);
01415        delete [] utf8str;
01416        return NS_OK;
01417 }
01418 
01419 NS_IMETHODIMP nsRenderingContextBeOS::CopyOffScreenBits(nsIDrawingSurface* aSrcSurf,
01420        PRInt32 aSrcX, PRInt32 aSrcY, const nsRect &aDestBounds, PRUint32 aCopyFlags)
01421 {
01422        
01423        PRInt32 srcX = aSrcX;
01424        PRInt32 srcY = aSrcY;
01425        nsRect drect = aDestBounds;
01426        
01427        if (aSrcSurf == nsnull)
01428               return NS_ERROR_FAILURE;
01429        if (mTranMatrix == nsnull)
01430               return NS_ERROR_FAILURE;
01431        if (mSurface == nsnull)
01432               return NS_ERROR_FAILURE;
01433               
01434        BView *srcview = nsnull;
01435        BView *destview = nsnull;
01436        BBitmap *srcbitmap = nsnull;
01437        nsDrawingSurfaceBeOS *srcsurf = nsnull;
01438        nsDrawingSurfaceBeOS *destsurf = nsnull;
01439        
01440        srcsurf = (nsDrawingSurfaceBeOS *)aSrcSurf;
01441        srcsurf->AcquireView(&srcview);
01442        srcsurf->AcquireBitmap(&srcbitmap);
01443        
01444                      
01445        if (aCopyFlags & NS_COPYBITS_TO_BACK_BUFFER) 
01446        {
01447               NS_ASSERTION(nsnull != mSurface, "no back buffer");
01448               destsurf = mSurface;
01449        } 
01450        else 
01451        {
01452               destsurf = mOffscreenSurface;
01453        }
01454                      
01455        if (!srcbitmap && srcsurf != mOffscreenSurface)
01456        {
01457 #ifdef DEBUG
01458               printf("nsRenderingContextBeOS::CopyOffScreenBits - FIXME: should render from surface without bitmap!?!?!\n");
01459 #endif
01460               if (srcview)
01461                      srcsurf->ReleaseView();
01462               return NS_OK;
01463        }
01464        
01465        destsurf->AcquireView(&destview);
01466 
01467        if (!destview)
01468        {
01469 #ifdef DEBUG
01470               printf("nsRenderingContextBeOS::CopyOffScreenBits - FIXME: no BView to draw!?!?!\n");
01471 #endif
01472               return NS_OK;
01473        }
01474 
01475        if (aCopyFlags & NS_COPYBITS_XFORM_SOURCE_VALUES) 
01476               mTranMatrix->TransformCoord(&srcX, &srcY);
01477        
01478        if (aCopyFlags & NS_COPYBITS_XFORM_DEST_VALUES)
01479                                    mTranMatrix->TransformCoord(&drect.x, &drect.y, &drect.width, &drect.height);
01480 
01481        if (!LockAndUpdateView())
01482        {
01483 #ifdef DEBUG
01484               printf("nsRenderingContextBeOS::CopyOffScreenBits - FIXME: no mVviewView - LockAndUpdate failed!\n");
01485 #endif
01486               return NS_OK;
01487        }
01488        // Locking for safety surface which wasn't locked above
01489        if (srcsurf != mSurface)
01490               srcsurf->LockDrawable();
01491        if (destsurf != mSurface)
01492               destsurf->LockDrawable();
01493        // Since all the drawing in this class is asynchronous, we must synchronize with
01494        // the app_server before drawing its contents anywhere else
01495        if (srcview)
01496               srcview->Sync();
01497 
01498        if (aCopyFlags & NS_COPYBITS_USE_SOURCE_CLIP_REGION) 
01499        {
01500               BRegion *region = nsnull;
01501               if(mClipRegion && mSurface == aSrcSurf)
01502                      mClipRegion->GetNativeRegion((void *&)region);
01503               // Following else-code has sense only if srcview and destview frames
01504               // are equal and is incompatible with #define NOBBCACHE.
01505               // Keeping it here for fallback case.
01506               else if(srcview->Bounds() == destview->Bounds())
01507                      srcview->GetClippingRegion(region);
01508               destview->ConstrainClippingRegion(region);
01509        }
01510                             
01511                             // Draw to destination synchronously to make sure srcbitmap doesn't change
01512                             // before the blit is finished.
01513        // TODO: look if we can blit directly in case of rendering to BDirectWindow, another buffer or self
01514        destview->DrawBitmap(srcbitmap, BRect(srcX, srcY, srcX + drect.width - 1, srcY + drect.height - 1),
01515               BRect(drect.x, drect.y, drect.x + drect.width - 1, drect.y + drect.height - 1));
01516        
01517        if (destsurf != mSurface)
01518               destsurf->UnlockDrawable();
01519        if (srcsurf != mSurface)
01520               srcsurf->UnlockDrawable();
01521        UnlockView();
01522        // Release. Fake at the moment.
01523        destsurf->ReleaseView();    
01524        srcsurf->ReleaseBitmap();   
01525        srcsurf->ReleaseView();
01526        return NS_OK;
01527 }
01528 
01529 NS_IMETHODIMP nsRenderingContextBeOS::RetrieveCurrentNativeGraphicData(void** ngd)
01530 {
01531        return NS_OK;
01532 }
01533 
01534 
01535 
01536 #ifdef MOZ_MATHML
01537 
01540 NS_IMETHODIMP 
01541 nsRenderingContextBeOS::GetBoundingMetrics(const char* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics)
01542 {
01543        aBoundingMetrics.Clear();
01544        if (0 >= aLength || !aString || !mCurrentBFont)
01545               return NS_ERROR_FAILURE;
01546 
01547        BRect rect;
01548        escapement_delta delta;
01549        delta.nonspace = 0;
01550        delta.space = 0;
01551        // Use the printing metric to get more detail
01552        mCurrentBFont->GetBoundingBoxesForStrings(&aString, 1, B_PRINTING_METRIC, &delta, &rect);
01553 
01554 
01555        GetWidth(aString, aLength, aBoundingMetrics.width );
01556        
01557        aBoundingMetrics.leftBearing = NSToCoordRound(rect.left * mP2T);
01558        aBoundingMetrics.rightBearing = NSToCoordRound(rect.right * mP2T);
01559 
01560        // The pen position for DrawString is at the baseline of the font for BeOS.  
01561        // The orientation for drawing moves downward for vertical metrics.
01562        // MathML expects what X Windows does: the orientation of the ascent moves upward from the baseline.
01563        // So, we need to negate the top value returned by GetBoundingBoxesForStrings, to have that value
01564        // move up in BeOS.
01565        aBoundingMetrics.ascent = NSToCoordRound((-rect.top) * mP2T);
01566        aBoundingMetrics.descent = NSToCoordRound(rect.bottom * mP2T);
01567 
01568        return NS_OK;
01569 }
01570 
01574 NS_IMETHODIMP 
01575 nsRenderingContextBeOS::GetBoundingMetrics(const PRUnichar* aString, PRUint32 aLength,
01576        nsBoundingMetrics& aBoundingMetrics, PRInt32* aFontID) 
01577 {
01578        aBoundingMetrics.Clear();
01579        nsresult r = NS_OK;
01580        if (0 < aLength)
01581        {
01582               if (aString == NULL)
01583                      return NS_ERROR_FAILURE;
01584 
01585               // Since more often than not, single char strings are passed to this function
01586               // we will try keep this on the stack, instead of the heap
01587               uint8 utf8buf[1024];
01588               uint8* utf8str = (uint8*)&utf8buf;
01589               if (aLength * 4 + 1 > 1024)
01590                      utf8str = new uint8[aLength * 4 + 1];
01591               uint8 *utf8ptr = utf8str;
01592               const PRUnichar *uniptr = aString;
01593        
01594               for (PRUint32 i = 0; i < aLength; i++)
01595               {
01596                      convert_to_utf8(utf8ptr, uniptr);
01597               }
01598        
01599               *utf8ptr = '\0';
01600               uint32 utf8str_len = strlen((char *)utf8str);
01601               r = GetBoundingMetrics((char *)utf8str, utf8str_len, aBoundingMetrics);
01602               if (utf8str != utf8buf)
01603                      delete [] utf8str;
01604               if (nsnull != aFontID)
01605                      *aFontID = 0;
01606        }             
01607        return r;
01608 }
01609 #endif /* MOZ_MATHML */
01610 
01611 #ifdef NOBBCACHE
01612 // Do not cache the backbuffer. Doesn't work in BeOS at the moment - cannot repaint
01613 // Window-attached BVIew. @see bug 95952 for other platforms
01614 NS_IMETHODIMP nsRenderingContextBeOS::GetBackbuffer(const nsRect &aRequestedSize,
01615                                                    const nsRect &aMaxSize,
01616                                                    PRBool aForBlending,
01617                                                    nsIDrawingSurface* &aBackbuffer)
01618 {
01619        return AllocateBackbuffer(aRequestedSize, aMaxSize, aBackbuffer, PR_FALSE, 0);
01620 }
01621  
01622 NS_IMETHODIMP nsRenderingContextBeOS::ReleaseBackbuffer(void)
01623 {
01624        return DestroyCachedBackbuffer();
01625 }
01626 #endif