Back to index

lightning-sunbird  0.9+nobinonly
nsRenderingContextOS2.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Mozilla OS/2 libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * John Fairhurst, <john_fairhurst@iname.com>.
00018  * Portions created by the Initial Developer are Copyright (C) 1999
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Henry Sobotka <sobotka@axess.com> Jan. 2000 review and update
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK *****
00037  *
00038  * This Original Code has been modified by IBM Corporation.
00039  * Modifications made by IBM described herein are
00040  * Copyright (c) International Business Machines
00041  * Corporation, 2000
00042  *
00043  * Modifications to Mozilla code or documentation
00044  * identified per MPL Section 3.3
00045  *
00046  * Date             Modified by     Description of modification
00047  * 03/23/2000       IBM Corp.      Fixed InvertRect.
00048  * 05/08/2000       IBM Corp.      Fix for trying to us an already freed mGammaTable.
00049  * 05/31/2000       IBM Corp.      Fix background color on PS printers.
00050  *
00051  */
00052 
00053 // ToDo: Unicode text draw-er
00054 #include "nsGfxDefs.h"
00055 
00056 #include "nsRenderingContextOS2.h"
00057 #include "nsFontMetricsOS2.h"
00058 #include "nsRegionOS2.h"
00059 #include "nsDeviceContextOS2.h"
00060 #include "prprf.h"
00061 #include "nsGfxCIID.h"
00062 #include "nsUnicharUtils.h"
00063 
00064 
00065 // helper clip region functions - defined at the bottom of this file.
00066 LONG OS2_CombineClipRegion( HPS hps, HRGN hrgnCombine, LONG lMode);
00067 HRGN OS2_CopyClipRegion( HPS hps);
00068 #define OS2_SetClipRegion(hps,hrgn) OS2_CombineClipRegion(hps, hrgn, CRGN_COPY)
00069 
00070 #define FLAG_CLIP_VALID       0x0001
00071 #define FLAG_CLIP_CHANGED     0x0002
00072 #define FLAG_LOCAL_CLIP_VALID 0x0004
00073 
00074 #define FLAGS_ALL             (FLAG_CLIP_VALID | FLAG_CLIP_CHANGED | FLAG_LOCAL_CLIP_VALID)
00075 
00076 class GraphicsState
00077 {
00078 public:
00079   GraphicsState();
00080   GraphicsState(GraphicsState &aState);
00081   ~GraphicsState();
00082 
00083   GraphicsState   *mNext;
00084   nsTransform2D   mMatrix;
00085   nsRect          mLocalClip;
00086   HRGN            mClipRegion;
00087   nscolor         mColor;
00088   nsIFontMetrics  *mFontMetrics;
00089   PRInt32         mFlags;
00090   nsLineStyle     mLineStyle;
00091 };
00092 
00093 GraphicsState :: GraphicsState()
00094 {
00095   mNext = nsnull;
00096   mMatrix.SetToIdentity();  
00097   mLocalClip.x = mLocalClip.y = mLocalClip.width = mLocalClip.height = 0;
00098   mClipRegion = NULL;
00099   mColor = NS_RGB(0, 0, 0);
00100   mFontMetrics = nsnull;
00101   mFlags = ~FLAGS_ALL;
00102   mLineStyle = nsLineStyle_kSolid;
00103 }
00104 
00105 GraphicsState :: GraphicsState(GraphicsState &aState) :
00106                                mMatrix(&aState.mMatrix),
00107                                mLocalClip(aState.mLocalClip)
00108 {
00109   mNext = &aState;
00110   mClipRegion = NULL;
00111   mColor = NS_RGB(0, 0, 0);
00112   mFontMetrics = nsnull;
00113   mFlags = ~FLAGS_ALL;
00114   mLineStyle = aState.mLineStyle;
00115 }
00116 
00117 GraphicsState :: ~GraphicsState()
00118 {
00119   if (NULL != mClipRegion)
00120   {
00121     printf( "oops, leaked a region from rc-gs\n");
00122     mClipRegion = NULL;
00123   }
00124 }
00125 
00126 // Rendering context -------------------------------------------------------
00127 
00128 // Init-term stuff ---------------------------------------------------------
00129 
00130 nsRenderingContextOS2::nsRenderingContextOS2()
00131 {
00132   mContext = nsnull;
00133   mSurface = nsnull;
00134   mPS = 0;
00135   mMainSurface = nsnull;
00136   mColor = NS_RGB( 0, 0, 0);
00137   mFontMetrics = nsnull;
00138   mLineStyle = nsLineStyle_kSolid;
00139   mPreservedInitialClipRegion = PR_FALSE;
00140   mPaletteMode = PR_FALSE;
00141   mCurrFontOS2 = nsnull;
00142 
00143   mStateCache = new nsVoidArray();
00144   mRightToLeftText = PR_FALSE;
00145 
00146   //create an initial GraphicsState
00147 
00148   PushState();
00149 
00150   mP2T = 1.0f;
00151 }
00152 
00153 nsRenderingContextOS2::~nsRenderingContextOS2()
00154 {
00155   NS_IF_RELEASE(mContext);
00156   NS_IF_RELEASE(mFontMetrics);
00157 
00158   //destroy the initial GraphicsState
00159   PopState ();
00160 
00161   if (nsnull != mStateCache)
00162   {
00163     PRInt32 cnt = mStateCache->Count();
00164 
00165     while (--cnt >= 0)
00166     {
00167       GraphicsState *state = (GraphicsState *)mStateCache->ElementAt(cnt);
00168       if (state->mClipRegion) {
00169          GFX (::GpiDestroyRegion (mPS, state->mClipRegion), FALSE);
00170          state->mClipRegion = 0;
00171       }
00172       mStateCache->RemoveElementAt(cnt);
00173 
00174       if (nsnull != state)
00175         delete state;
00176     }
00177 
00178     delete mStateCache;
00179     mStateCache = nsnull;
00180   }
00181 
00182    // Release surfaces and the palette
00183    NS_IF_RELEASE(mMainSurface);
00184    NS_IF_RELEASE(mSurface);
00185 }
00186 
00187 nsresult
00188 nsRenderingContextOS2::QueryInterface( REFNSIID aIID, void **aInstancePtr)
00189 {
00190    if( !aInstancePtr)
00191       return NS_ERROR_NULL_POINTER;
00192 
00193    if( aIID.Equals( nsIRenderingContext::GetIID()))
00194       *aInstancePtr = (void *) (nsIRenderingContext*) this;
00195    else if( aIID.Equals( ((nsIRenderingContext*)this)->GetIID()))
00196       *aInstancePtr = (void *) (nsIRenderingContext*)this;
00197    else if( aIID.Equals( nsIRenderingContextOS2::GetIID()))
00198       *aInstancePtr = (void *) (nsIRenderingContextOS2*) this;
00199    else if( aIID.Equals( ((nsIRenderingContextOS2 *)this)->GetIID()))
00200       *aInstancePtr = (void *) (nsIRenderingContextOS2*) this;
00201 
00202    if( !*aInstancePtr)
00203       return NS_NOINTERFACE;
00204 
00205    NS_ADDREF_THIS();
00206 
00207    return NS_OK;
00208 }
00209 
00210 NS_IMPL_ADDREF(nsRenderingContextOS2)
00211 NS_IMPL_RELEASE(nsRenderingContextOS2)
00212 
00213 NS_IMETHODIMP
00214 nsRenderingContextOS2::Init( nsIDeviceContext *aContext,
00215                              nsIWidget *aWindow)
00216 {
00217    mContext = aContext;
00218    NS_IF_ADDREF(mContext);
00219 
00220    // Create & remember an on-screen surface
00221    nsWindowSurface *surf = new nsWindowSurface;
00222    if (!surf)
00223      return NS_ERROR_OUT_OF_MEMORY;
00224 
00225    surf->Init(aWindow);
00226 
00227    mSurface = surf;
00228    mPS = mSurface->GetPS ();
00229    NS_ADDREF(mSurface);
00230 
00231    mDCOwner = aWindow;
00232    NS_IF_ADDREF(mDCOwner);
00233 
00234    // Grab another reference to the onscreen for later uniformity
00235    mMainSurface = mSurface;
00236    NS_ADDREF(mMainSurface);
00237 
00238    return CommonInit();
00239 }
00240 
00241 NS_IMETHODIMP
00242 nsRenderingContextOS2::Init( nsIDeviceContext *aContext,
00243                              nsIDrawingSurface* aSurface)
00244 {
00245    mContext = aContext;
00246    NS_IF_ADDREF(mContext);
00247 
00248    // Add a couple of references to the onscreen (or print, more likely)
00249    mSurface = (nsDrawingSurfaceOS2 *) aSurface;
00250 
00251   if (nsnull != mSurface)
00252   {
00253     mPS = mSurface->GetPS ();
00254     NS_ADDREF(mSurface);
00255 
00256     mMainSurface = mSurface;
00257     NS_ADDREF(mMainSurface);
00258   }
00259 
00260    return CommonInit();
00261 }
00262 
00263 nsresult nsRenderingContextOS2::SetupPS(void)
00264 {
00265    LONG BlackColor, WhiteColor;
00266 
00267    // If this is a palette device, then set transparent colors
00268    if (((nsDeviceContextOS2*)mContext)->IsPaletteDevice())
00269    {
00270       BlackColor = GFX (::GpiQueryColorIndex(mPS, 0, MK_RGB (0x00, 0x00, 0x00)), GPI_ALTERROR);    // CLR_BLACK;
00271       WhiteColor = GFX (::GpiQueryColorIndex(mPS, 0, MK_RGB (0xFF, 0xFF, 0xFF)), GPI_ALTERROR);    // CLR_WHITE;
00272 
00273       mPaletteMode = PR_TRUE;
00274    }
00275    else
00276    {
00277       GFX (::GpiCreateLogColorTable(mPS, 0, LCOLF_RGB, 0, 0, 0), FALSE);
00278 
00279       BlackColor = MK_RGB(0x00, 0x00, 0x00);
00280       WhiteColor = MK_RGB(0xFF, 0xFF, 0xFF);
00281 
00282       mPaletteMode = PR_FALSE;
00283    }
00284 
00285    // Set image foreground and background colors. These are used in transparent images for blitting 1-bit masks.
00286    // To invert colors on ROP_SRCAND we map 1 to black and 0 to white
00287    IMAGEBUNDLE ib;
00288    ib.lColor     = BlackColor;           // map 1 in mask to 0x000000 (black) in destination
00289    ib.lBackColor = WhiteColor;           // map 0 in mask to 0xFFFFFF (white) in destination
00290    ib.usMixMode  = FM_OVERPAINT;
00291    ib.usBackMixMode = BM_OVERPAINT;
00292    GFX (::GpiSetAttrs(mPS, PRIM_IMAGE, IBB_COLOR | IBB_BACK_COLOR | IBB_MIX_MODE | IBB_BACK_MIX_MODE, 0, (PBUNDLE)&ib), FALSE);
00293 
00294    return NS_OK;
00295 }
00296 
00297 // Presentation space page units (& so world coords) are PU_PELS.
00298 // We have a matrix, mTMatrix, which converts from the passed in app units
00299 // to pels.  Note there is *no* guarantee that app units == twips.
00300 nsresult nsRenderingContextOS2::CommonInit()
00301 {
00302    float app2dev;
00303 
00304    app2dev = mContext->AppUnitsToDevUnits();
00305    mTranMatrix->AddScale( app2dev, app2dev);
00306    mP2T = mContext->DevUnitsToAppUnits();
00307 
00308    return SetupPS();
00309 }
00310 
00311 // PS & drawing surface management -----------------------------------------
00312 
00313 NS_IMETHODIMP nsRenderingContextOS2::LockDrawingSurface( PRInt32 aX, PRInt32 aY,
00314                                        PRUint32 aWidth, PRUint32 aHeight,
00315                                        void **aBits,
00316                                        PRInt32 *aStride, PRInt32 *aWidthBytes,
00317                                        PRUint32 aFlags)
00318 {
00319   PushState();
00320 
00321   return mSurface->Lock( aX, aY, aWidth, aHeight, aBits,
00322                          aStride, aWidthBytes, aFlags);
00323 }
00324 
00325 NS_IMETHODIMP nsRenderingContextOS2::UnlockDrawingSurface()
00326 {
00327   mSurface->Unlock();
00328 
00329   PopState();
00330 
00331   return NS_OK;
00332 }
00333 
00334 NS_IMETHODIMP
00335 nsRenderingContextOS2::SelectOffScreenDrawingSurface( nsIDrawingSurface* aSurface)
00336 {
00337    if (aSurface != mSurface)
00338    {
00339       if(nsnull != aSurface)
00340       {
00341          NS_IF_RELEASE(mSurface);
00342          mSurface = (nsDrawingSurfaceOS2 *) aSurface;
00343          mPS = mSurface->GetPS ();
00344 
00345          SetupPS();
00346       }
00347       else // deselect current offscreen...
00348       {
00349          NS_IF_RELEASE(mSurface);
00350          mSurface = mMainSurface;
00351          mPS = mSurface->GetPS ();
00352 
00353          SetupPS();
00354       }
00355 
00356       NS_ADDREF(mSurface);
00357    }
00358 
00359    return NS_OK;
00360 }
00361 
00362 NS_IMETHODIMP
00363 nsRenderingContextOS2::GetDrawingSurface( nsIDrawingSurface* *aSurface)
00364 {
00365    *aSurface = mSurface;
00366    return NS_OK;
00367 }
00368 
00369 NS_IMETHODIMP
00370 nsRenderingContextOS2::GetHints(PRUint32& aResult)
00371 {
00372   PRUint32 result = 0;
00373   
00374   result |= NS_RENDERING_HINT_FAST_MEASURE;
00375 
00376   aResult = result;
00377   return NS_OK;
00378 }
00379 
00380 NS_IMETHODIMP nsRenderingContextOS2::Reset()
00381 {
00382    return NS_OK;
00383 }
00384 
00385 NS_IMETHODIMP nsRenderingContextOS2::GetDeviceContext( nsIDeviceContext *&aContext)
00386 {
00387   NS_IF_ADDREF(mContext);
00388   aContext = mContext;
00389   return NS_OK;
00390 }
00391 
00392 NS_IMETHODIMP nsRenderingContextOS2 :: PushState(void)
00393 {
00394   PRInt32 cnt = mStateCache->Count();
00395 
00396   if (cnt == 0)
00397   {
00398     if (nsnull == mStates)
00399       mStates = new GraphicsState();
00400     else
00401       mStates = new GraphicsState(*mStates);
00402   }
00403   else
00404   {
00405     GraphicsState *state = (GraphicsState *)mStateCache->ElementAt(cnt - 1);
00406     mStateCache->RemoveElementAt(cnt - 1);
00407 
00408     state->mNext = mStates;
00409 
00410     //clone state info
00411 
00412     state->mMatrix = mStates->mMatrix;
00413     state->mLocalClip = mStates->mLocalClip;
00414 // we don't want to NULL this out since we reuse the region
00415 // from state to state. if we NULL it, we need to also delete it,
00416 // which means we'll just re-create it when we push the clip state. MMP
00417 //   state->mClipRegion = OS2_CopyClipRegion( mPS);
00418     state->mFlags = ~FLAGS_ALL;
00419     state->mLineStyle = mStates->mLineStyle;
00420 
00421     mStates = state;
00422   }
00423 
00424   if (nsnull != mStates->mNext)
00425   {
00426     mStates->mNext->mColor = mColor;
00427     mStates->mNext->mFontMetrics = mFontMetrics;
00428     NS_IF_ADDREF(mStates->mNext->mFontMetrics);
00429   }
00430 
00431   mTranMatrix = &mStates->mMatrix;
00432 
00433   return NS_OK;
00434 }
00435 
00436 NS_IMETHODIMP nsRenderingContextOS2 :: PopState(void)
00437 {
00438   if (nsnull == mStates)
00439   {
00440     NS_ASSERTION(!(nsnull == mStates), "state underflow");
00441   }
00442   else
00443   {
00444     GraphicsState *oldstate = mStates;
00445 
00446     mStates = mStates->mNext;
00447 
00448     mStateCache->AppendElement(oldstate);
00449 
00450     if (nsnull != mStates)
00451     {
00452       mTranMatrix = &mStates->mMatrix;
00453 
00454       GraphicsState *pstate;
00455 
00456       if (oldstate->mFlags & FLAG_CLIP_CHANGED)
00457       {
00458         pstate = mStates;
00459 
00460         //the clip rect has changed from state to state, so
00461         //install the previous clip rect
00462 
00463         while ((nsnull != pstate) && !(pstate->mFlags & FLAG_CLIP_VALID))
00464           pstate = pstate->mNext;
00465 
00466         if (nsnull != pstate)
00467         {
00468           // set copy of pstate->mClipRegion as current clip region
00469           HRGN hrgn = GFX (::GpiCreateRegion (mPS, 0, NULL), RGN_ERROR);
00470           GFX (::GpiCombineRegion (mPS, hrgn, pstate->mClipRegion, 0, CRGN_COPY), RGN_ERROR);
00471           OS2_SetClipRegion (mPS, hrgn);
00472         }
00473       }
00474 
00475       oldstate->mFlags &= ~FLAGS_ALL;
00476 
00477       NS_IF_RELEASE(mFontMetrics);
00478       mFontMetrics = mStates->mFontMetrics;
00479 
00480       mColor = mStates->mColor;
00481 
00482       SetLineStyle(mStates->mLineStyle);
00483     }
00484     else
00485       mTranMatrix = nsnull;
00486   }
00487 
00488   return NS_OK;
00489 }
00490 
00491 NS_IMETHODIMP nsRenderingContextOS2::IsVisibleRect( const nsRect &aRect,
00492                                                     PRBool &aIsVisible)
00493 {
00494    nsRect trect( aRect);
00495    mTranMatrix->TransformCoord( &trect.x, &trect.y,
00496                             &trect.width, &trect.height);
00497    RECTL rcl;
00498    mSurface->NS2PM_ININ (trect, rcl);
00499 
00500    LONG rc = GFX (::GpiRectVisible( mPS, &rcl), RVIS_ERROR);
00501 
00502    aIsVisible = (rc == RVIS_PARTIAL || rc == RVIS_VISIBLE) ? PR_TRUE : PR_FALSE;
00503 
00504 
00505    return NS_OK;
00506 }
00507 
00508 // Return PR_TRUE if clip region is now empty
00509 NS_IMETHODIMP nsRenderingContextOS2::SetClipRect( const nsRect& aRect, nsClipCombine aCombine)
00510 {
00511   nsRect  trect = aRect;
00512   RECTL   rcl;
00513 
00514   mTranMatrix->TransformCoord(&trect.x, &trect.y,
00515                            &trect.width, &trect.height);
00516 
00517   mStates->mLocalClip = aRect;
00518   mStates->mFlags |= FLAG_LOCAL_CLIP_VALID;
00519 
00520   //how we combine the new rect with the previous?
00521 
00522   if( trect.width == 0 || trect.height == 0)
00523   {
00524     // Mozilla assumes that clip region with zero width or height does not produce any output - everything is clipped.
00525     // GPI does not support clip regions with zero width or height. We cheat by creating 1x1 clip region far outside
00526     // of real drawing region limits.
00527 
00528     if( aCombine == nsClipCombine_kIntersect || aCombine == nsClipCombine_kReplace)
00529     {
00530       PushClipState();
00531 
00532       rcl.xLeft   = -10000;
00533       rcl.xRight  = -9999;
00534       rcl.yBottom = -10000;
00535       rcl.yTop    = -9999;
00536 
00537       HRGN hrgn = GFX (::GpiCreateRegion( mPS, 1, &rcl), RGN_ERROR);
00538       OS2_SetClipRegion (mPS, hrgn);
00539     }
00540     else if( aCombine == nsClipCombine_kUnion || aCombine == nsClipCombine_kSubtract)
00541     {
00542       PushClipState();
00543 
00544       // Clipping region is already correct. Just need to obtain it's complexity
00545       POINTL Offset = { 0, 0 };
00546 
00547       GFX (::GpiOffsetClipRegion (mPS, &Offset), RGN_ERROR);
00548     }
00549     else
00550       NS_ASSERTION(PR_FALSE, "illegal clip combination");
00551   }
00552   else
00553   {
00554     if (aCombine == nsClipCombine_kIntersect)
00555     {
00556       PushClipState();
00557 
00558       mSurface->NS2PM_ININ (trect, rcl);
00559       GFX (::GpiIntersectClipRectangle(mPS, &rcl), RGN_ERROR);
00560     }
00561     else if (aCombine == nsClipCombine_kUnion)
00562     {
00563       PushClipState();
00564 
00565       mSurface->NS2PM_INEX (trect, rcl);
00566       HRGN hrgn = GFX (::GpiCreateRegion(mPS, 1, &rcl), RGN_ERROR);
00567 
00568       if( hrgn )
00569         OS2_CombineClipRegion(mPS, hrgn, CRGN_OR);
00570     }
00571     else if (aCombine == nsClipCombine_kSubtract)
00572     {
00573       PushClipState();
00574 
00575       mSurface->NS2PM_ININ (trect, rcl);
00576       GFX (::GpiExcludeClipRectangle(mPS, &rcl), RGN_ERROR);
00577     }
00578     else if (aCombine == nsClipCombine_kReplace)
00579     {
00580       PushClipState();
00581 
00582       mSurface->NS2PM_INEX (trect, rcl);
00583       HRGN hrgn = GFX (::GpiCreateRegion(mPS, 1, &rcl), RGN_ERROR);
00584 
00585       if( hrgn )
00586        OS2_SetClipRegion(mPS, hrgn);
00587     }
00588     else
00589       NS_ASSERTION(PR_FALSE, "illegal clip combination");
00590   }
00591 
00592   return NS_OK;
00593 }
00594 
00595 // rc is whether there is a cliprect to return
00596 NS_IMETHODIMP nsRenderingContextOS2::GetClipRect( nsRect &aRect, PRBool &aClipValid)
00597 {
00598   if (mStates->mFlags & FLAG_LOCAL_CLIP_VALID)
00599   {
00600     aRect = mStates->mLocalClip;
00601     aClipValid = PR_TRUE;
00602   }
00603   else
00604     aClipValid = PR_FALSE;
00605 
00606   return NS_OK;
00607 }
00608 
00609 // Return PR_TRUE if clip region is now empty
00610 NS_IMETHODIMP nsRenderingContextOS2::SetClipRegion( const nsIRegion &aRegion, nsClipCombine aCombine)
00611 {
00612    nsRegionOS2 *pRegion = (nsRegionOS2 *) &aRegion;
00613    PRUint32     ulHeight = mSurface->GetHeight ();
00614 
00615    HRGN hrgn = pRegion->GetHRGN( ulHeight, mPS);
00616    LONG cmode = 0L;
00617 
00618    switch( aCombine)
00619    {
00620       case nsClipCombine_kIntersect:
00621          cmode = CRGN_AND;
00622          break;
00623       case nsClipCombine_kUnion:
00624          cmode = CRGN_OR;
00625          break;
00626       case nsClipCombine_kSubtract:
00627          cmode = CRGN_DIFF;
00628          break;
00629       case nsClipCombine_kReplace:
00630          cmode = CRGN_COPY;
00631          break;
00632       default:
00633          // Compiler informational...
00634          NS_ASSERTION( 0, "illegal clip combination");
00635          break;
00636   }
00637 
00638   if (NULL != hrgn)
00639   {
00640     mStates->mFlags &= ~FLAG_LOCAL_CLIP_VALID;
00641     PushClipState();
00642     OS2_CombineClipRegion( mPS, hrgn, cmode);
00643   }
00644   else
00645     return PR_FALSE;
00646 
00647   return NS_OK;
00648 }
00649 
00653 NS_IMETHODIMP nsRenderingContextOS2::CopyClipRegion(nsIRegion &aRegion)
00654 {
00655 #if 0
00656   HRGN hr = OS2_CopyClipRegion(mPS);
00657 
00658   if (hr == HRGN_ERROR)
00659     return NS_ERROR_FAILURE;
00660 
00661   ((nsRegionOS2 *)&aRegion)->mRegion = hr;
00662 
00663   return NS_OK;
00664 #else
00665   NS_ASSERTION( 0, "nsRenderingContextOS2::CopyClipRegion() not implemented" );
00666   return NS_ERROR_NOT_IMPLEMENTED;
00667 #endif
00668 }
00669 
00670 // Somewhat dubious & rather expensive
00671 NS_IMETHODIMP nsRenderingContextOS2::GetClipRegion( nsIRegion **aRegion)
00672 {
00673    if( !aRegion)
00674       return NS_ERROR_NULL_POINTER;
00675 
00676    *aRegion = 0;
00677 
00678    nsRegionOS2 *pRegion = new nsRegionOS2;
00679    if (!pRegion)
00680      return NS_ERROR_OUT_OF_MEMORY;
00681    NS_ADDREF(pRegion);
00682 
00683    // Get current clip region
00684    HRGN hrgnClip = 0;
00685 
00686    GFX (::GpiSetClipRegion (mPS, 0, &hrgnClip), RGN_ERROR);
00687    
00688    if( hrgnClip && hrgnClip != HRGN_ERROR)
00689    {
00690       // There was a clip region, so get it & init.
00691       HRGN hrgnDummy = 0;
00692       PRUint32 ulHeight = mSurface->GetHeight ();
00693 
00694       pRegion->InitWithHRGN (hrgnClip, ulHeight, mPS);
00695       GFX (::GpiSetClipRegion (mPS, hrgnClip, &hrgnDummy), RGN_ERROR);
00696    }
00697    else
00698       pRegion->Init();
00699 
00700    *aRegion = pRegion;
00701 
00702    return NS_OK;
00703 }
00704 
00705 NS_IMETHODIMP nsRenderingContextOS2::SetColor( nscolor aColor)
00706 {
00707    mColor = aColor;
00708    return NS_OK;
00709 }
00710 
00711 NS_IMETHODIMP nsRenderingContextOS2::GetColor( nscolor &aColor) const
00712 {
00713    aColor = mColor;
00714    return NS_OK;
00715 }
00716 
00717 NS_IMETHODIMP nsRenderingContextOS2::SetLineStyle( nsLineStyle aLineStyle)
00718 {
00719    mLineStyle = aLineStyle;
00720    return NS_OK;
00721 }
00722 
00723 NS_IMETHODIMP nsRenderingContextOS2::GetLineStyle( nsLineStyle &aLineStyle)
00724 {
00725    aLineStyle = mLineStyle;
00726    return NS_OK;
00727 }
00728 
00729 NS_IMETHODIMP nsRenderingContextOS2::SetFont( const nsFont &aFont, nsIAtom* aLangGroup)
00730 {
00731   mCurrFontOS2 = nsnull; // owned & released by mFontMetrics
00732   NS_IF_RELEASE(mFontMetrics);
00733   mContext->GetMetricsFor(aFont, aLangGroup, mFontMetrics);
00734 
00735   return NS_OK;
00736 }
00737 
00738 NS_IMETHODIMP nsRenderingContextOS2::SetFont( nsIFontMetrics *aFontMetrics)
00739 {
00740   mCurrFontOS2 = nsnull; // owned & released by mFontMetrics
00741   NS_IF_RELEASE(mFontMetrics);
00742   mFontMetrics = aFontMetrics;
00743   NS_IF_ADDREF(mFontMetrics);
00744 
00745   return NS_OK;
00746 }
00747 
00748 NS_IMETHODIMP nsRenderingContextOS2::GetFontMetrics( nsIFontMetrics*& aFontMetrics)
00749 {
00750   NS_IF_ADDREF(mFontMetrics);
00751   aFontMetrics = mFontMetrics;
00752 
00753   return NS_OK;
00754 }
00755 
00756 // add the passed in translation to the current translation
00757 NS_IMETHODIMP nsRenderingContextOS2::Translate( nscoord aX, nscoord aY)
00758 {
00759    mTranMatrix->AddTranslation( (float) aX, (float) aY);
00760    return NS_OK;
00761 }
00762 
00763 // add the passed in scale to the current scale
00764 NS_IMETHODIMP nsRenderingContextOS2::Scale( float aSx, float aSy)
00765 {
00766    mTranMatrix->AddScale(aSx, aSy);
00767    return NS_OK;
00768 }
00769 
00770 NS_IMETHODIMP nsRenderingContextOS2::GetCurrentTransform( nsTransform2D *&aTransform)
00771 {
00772   aTransform = mTranMatrix;
00773   return NS_OK;
00774 }
00775 
00776 
00777 // The idea here is to create an offscreen surface for blitting with.
00778 // I can't find any way for people to resize the bitmap created here,
00779 // so I guess this gets called quite often.
00780 //
00781 // I'm reliably told that 'aBounds' is in device units, and that the
00782 // position oughtn't to be ignored, but for all intents & purposes can be.
00783 //
00784 NS_IMETHODIMP nsRenderingContextOS2::CreateDrawingSurface(const nsRect& aBounds,
00785                              PRUint32 aSurfFlags, nsIDrawingSurface* &aSurface)
00786 {
00787    nsresult rc = NS_ERROR_FAILURE;
00788 
00789    nsOffscreenSurface *surf = new nsOffscreenSurface;
00790 
00791    if (!surf)
00792      return NS_ERROR_OUT_OF_MEMORY;
00793 
00794    rc = surf->Init( mMainSurface->GetPS (), aBounds.width, aBounds.height, aSurfFlags);
00795 
00796    if(NS_SUCCEEDED(rc))
00797    {
00798       NS_ADDREF(surf);
00799       aSurface = surf;
00800    }
00801    else
00802       delete surf;
00803 
00804    return rc;
00805 }
00806 
00807 NS_IMETHODIMP nsRenderingContextOS2::CreateDrawingSurface(HPS aPS, nsIDrawingSurface* &aSurface, nsIWidget *aWidget)
00808 {
00809   nsWindowSurface *surf = new nsWindowSurface();
00810 
00811   if (nsnull != surf)
00812   {
00813     NS_ADDREF(surf);
00814     surf->Init(aPS, aWidget);
00815   }
00816 
00817   aSurface = surf;
00818 
00819   return NS_OK;
00820 }
00821 
00822 NS_IMETHODIMP nsRenderingContextOS2::DestroyDrawingSurface( nsIDrawingSurface* aDS)
00823 {
00824    nsDrawingSurfaceOS2 *surf = (nsDrawingSurfaceOS2 *) aDS;
00825    nsresult rc = NS_ERROR_NULL_POINTER;
00826 
00827    // If the surface being destroyed is the one we're currently using for
00828    // offscreen, then lose our reference to it & hook back to the onscreen.
00829    if( surf && surf == mSurface)
00830    {
00831       NS_RELEASE(mSurface);    // ref. from SelectOffscreen
00832       mSurface = mMainSurface;
00833       mPS = mSurface->GetPS ();
00834       NS_ADDREF(mSurface);
00835    }
00836 
00837    if( surf)
00838    {
00839       NS_RELEASE(surf);        // ref. from CreateSurface
00840       rc = NS_OK;
00841    }
00842 
00843    return rc;
00844 }
00845 
00846 // Drawing methods ---------------------------------------------------------
00847 
00848 LONG nsRenderingContextOS2::GetGPIColor (void)
00849 {
00850    LONG gcolor = MK_RGB (NS_GET_R (mColor),
00851                          NS_GET_G (mColor),
00852                          NS_GET_B (mColor));
00853 
00854    return (mPaletteMode) ? GFX (::GpiQueryColorIndex (mPS, 0, gcolor), GPI_ALTERROR) :
00855                            gcolor ;
00856 }
00857 
00858 void nsRenderingContextOS2::SetupLineColorAndStyle (void)
00859 {
00860    
00861    LINEBUNDLE lineBundle;
00862    lineBundle.lColor = GetGPIColor ();
00863 
00864    GFX (::GpiSetAttrs (mPS, PRIM_LINE, LBB_COLOR, 0, (PBUNDLE)&lineBundle), FALSE);
00865    
00866    long ltype = 0;
00867    switch( mLineStyle)
00868    {
00869       case nsLineStyle_kNone:   ltype = LINETYPE_INVISIBLE; break;
00870       case nsLineStyle_kSolid:  ltype = LINETYPE_SOLID; break;
00871       case nsLineStyle_kDashed: ltype = LINETYPE_SHORTDASH; break;
00872       case nsLineStyle_kDotted: ltype = LINETYPE_DOT; break;
00873       default:
00874          NS_ASSERTION(0, "Unexpected line style");
00875          break;
00876    }
00877    GFX (::GpiSetLineType (mPS, ltype), FALSE);
00878    
00879 }
00880 
00881 void nsRenderingContextOS2::SetupFillColor (void)
00882 {
00883    
00884    AREABUNDLE areaBundle;
00885    areaBundle.lColor = GetGPIColor ();
00886 
00887    GFX (::GpiSetAttrs (mPS, PRIM_AREA, ABB_COLOR, 0, (PBUNDLE)&areaBundle), FALSE);
00888 
00889 }
00890 
00891 NS_IMETHODIMP nsRenderingContextOS2::DrawLine( nscoord aX0, nscoord aY0, nscoord aX1, nscoord aY1)
00892 {
00893    mTranMatrix->TransformCoord( &aX0, &aY0);
00894    mTranMatrix->TransformCoord( &aX1, &aY1);
00895 
00896    POINTL ptls[] = { { (long) aX0, (long) aY0 },
00897                      { (long) aX1, (long) aY1 } };
00898    mSurface->NS2PM (ptls, 2);
00899 
00900    if (ptls[0].x > ptls[1].x)
00901       ptls[0].x--;
00902    else if (ptls[1].x > ptls[0].x)
00903       ptls[1].x--;
00904 
00905    if (ptls[0].y < ptls[1].y)
00906       ptls[0].y++;
00907    else if (ptls[1].y < ptls[0].y)
00908       ptls[1].y++;
00909 
00910    SetupLineColorAndStyle ();
00911 
00912    GFX (::GpiMove (mPS, ptls), FALSE);
00913    GFX (::GpiLine (mPS, ptls + 1), GPI_ERROR);
00914 
00915    return NS_OK;
00916 }
00917 
00918 NS_IMETHODIMP nsRenderingContextOS2::DrawPolyline(const nsPoint aPoints[], PRInt32 aNumPoints)
00919 {
00920    PMDrawPoly( aPoints, aNumPoints, PR_FALSE);
00921    return NS_OK;
00922 }
00923 
00924 NS_IMETHODIMP nsRenderingContextOS2::DrawPolygon( const nsPoint aPoints[], PRInt32 aNumPoints)
00925 {
00926    PMDrawPoly( aPoints, aNumPoints, PR_FALSE);
00927    return NS_OK;
00928 }
00929 
00930 NS_IMETHODIMP nsRenderingContextOS2::FillPolygon( const nsPoint aPoints[], PRInt32 aNumPoints)
00931 {
00932    PMDrawPoly( aPoints, aNumPoints, PR_TRUE );
00933    return NS_OK;
00934 }
00935 
00936  // bDoTransform defaults to PR_TRUE
00937 void nsRenderingContextOS2::PMDrawPoly( const nsPoint aPoints[], PRInt32 aNumPoints, PRBool bFilled, PRBool bDoTransform)
00938 {
00939    if( aNumPoints > 1)
00940    {
00941       // Xform coords
00942       POINTL  aptls[ 20];
00943       PPOINTL pts = aptls;
00944 
00945       if( aNumPoints > 20)
00946          pts = new POINTL[aNumPoints];
00947 
00948       PPOINTL pp = pts;
00949       const nsPoint *np = &aPoints[0];
00950 
00951       for( PRInt32 i = 0; i < aNumPoints; i++, pp++, np++)
00952       {
00953          pp->x = np->x;
00954          pp->y = np->y;
00955          if( bDoTransform )
00956            mTranMatrix->TransformCoord( (int*)&pp->x, (int*)&pp->y);
00957       }
00958 
00959       // go to os2
00960       mSurface->NS2PM (pts, aNumPoints);
00961 
00962       // We draw closed pgons using polyline to avoid filling it.  This works
00963       // because the API to this class specifies that the last point must
00964       // be the same as the first one...
00965 
00966       GFX (::GpiMove (mPS, pts), FALSE);
00967 
00968       if( bFilled == PR_TRUE)
00969       {
00970          POLYGON pgon = { aNumPoints - 1, pts + 1 };
00971          //IBM-AKR changed from boundary and inclusive to be noboundary and 
00972          //        exclusive to fix bug with text fields, buttons, etc. borders 
00973          //        being 1 pel too thick.  Bug 56853
00974          SetupFillColor ();
00975          GFX (::GpiPolygons (mPS, 1, &pgon, POLYGON_NOBOUNDARY, POLYGON_EXCL), GPI_ERROR);
00976       }
00977       else
00978       {
00979          SetupLineColorAndStyle ();
00980          GFX (::GpiPolyLine (mPS, aNumPoints - 1, pts + 1), GPI_ERROR);
00981       }
00982 
00983       if( aNumPoints > 20)
00984          delete [] pts;
00985    }
00986 }
00987 
00988 NS_IMETHODIMP nsRenderingContextOS2::DrawRect( const nsRect& aRect)
00989 {
00990    nsRect tr = aRect;
00991    PMDrawRect( tr, FALSE);
00992    return NS_OK;
00993 }
00994 
00995 NS_IMETHODIMP nsRenderingContextOS2::DrawRect( nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00996 {
00997    nsRect tr( aX, aY, aWidth, aHeight);
00998    PMDrawRect( tr, FALSE);
00999    return NS_OK;
01000 }
01001 
01002 NS_IMETHODIMP nsRenderingContextOS2::FillRect( const nsRect& aRect)
01003 {
01004    nsRect tr = aRect;
01005    PMDrawRect( tr, TRUE);
01006    return NS_OK;
01007 }
01008 
01009 NS_IMETHODIMP nsRenderingContextOS2::FillRect( nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
01010 {
01011    nsRect tr( aX, aY, aWidth, aHeight);
01012    PMDrawRect( tr, TRUE);
01013    return NS_OK;
01014 }
01015 
01016 NS_IMETHODIMP
01017 nsRenderingContextOS2 :: InvertRect(const nsRect& aRect)
01018 {
01019    return InvertRect(aRect.x, aRect.y, aRect.width, aRect.height);
01020 }
01021 
01022 NS_IMETHODIMP
01023 nsRenderingContextOS2 :: InvertRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
01024 {
01025    nsRect tr(aX, aY, aWidth, aHeight);
01026    LONG CurMix = GFX (::GpiQueryMix (mPS), FM_ERROR);
01027    GFX (::GpiSetMix (mPS, FM_INVERT), FALSE);
01028    PMDrawRect(tr, FALSE);
01029    GFX (::GpiSetMix (mPS, CurMix), FALSE);
01030    return NS_OK;
01031 }
01032 
01033 void nsRenderingContextOS2::PMDrawRect( nsRect &rect, BOOL fill)
01034 {
01035    mTranMatrix->TransformCoord( &rect.x, &rect.y, &rect.width, &rect.height);
01036 
01037    // only draw line if it has a non-zero height and width
01038    if ( !rect.width || !rect.height )
01039       return;
01040 
01041    RECTL rcl;
01042    mSurface->NS2PM_ININ (rect, rcl);
01043 
01044    GFX (::GpiMove (mPS, (PPOINTL) &rcl), FALSE);
01045 
01046    if (rcl.xLeft == rcl.xRight || rcl.yTop == rcl.yBottom)
01047    {
01048       SetupLineColorAndStyle ();
01049       GFX (::GpiLine (mPS, ((PPOINTL)&rcl) + 1), GPI_ERROR);
01050    }
01051    else 
01052    {
01053       long lOps;
01054 
01055       if (fill)
01056       {
01057          lOps = DRO_FILL;
01058          SetupFillColor ();
01059       } else
01060       {
01061          lOps = DRO_OUTLINE;
01062          SetupLineColorAndStyle ();
01063       }
01064 
01065       GFX (::GpiBox (mPS, lOps, ((PPOINTL)&rcl) + 1, 0, 0), GPI_ERROR);
01066    }
01067 }
01068 
01069 // Arc-drawing methods, all proxy on to PMDrawArc
01070 NS_IMETHODIMP nsRenderingContextOS2::DrawEllipse( const nsRect& aRect)
01071 {
01072    nsRect tRect( aRect);
01073    PMDrawArc( tRect, PR_FALSE, PR_TRUE);
01074    return NS_OK;
01075 }
01076 
01077 NS_IMETHODIMP nsRenderingContextOS2::DrawEllipse( nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
01078 {
01079    nsRect tRect( aX, aY, aWidth, aHeight);
01080    PMDrawArc( tRect, PR_FALSE, PR_TRUE);
01081    return NS_OK;
01082 }
01083 
01084 NS_IMETHODIMP nsRenderingContextOS2::FillEllipse( const nsRect& aRect)
01085 {
01086    nsRect tRect( aRect);
01087    PMDrawArc( tRect, PR_TRUE, PR_TRUE);
01088    return NS_OK;
01089 }
01090 
01091 NS_IMETHODIMP nsRenderingContextOS2::FillEllipse( nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
01092 {
01093    nsRect tRect( aX, aY, aWidth, aHeight);
01094    PMDrawArc( tRect, PR_TRUE, PR_TRUE);
01095    return NS_OK;
01096 }
01097 
01098 NS_IMETHODIMP nsRenderingContextOS2::DrawArc( const nsRect& aRect,
01099                                          float aStartAngle, float aEndAngle)
01100 {
01101    nsRect tRect( aRect);
01102    PMDrawArc( tRect, PR_FALSE, PR_FALSE, aStartAngle, aEndAngle);
01103    return NS_OK;
01104 }
01105 
01106 NS_IMETHODIMP nsRenderingContextOS2::DrawArc( nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
01107                                          float aStartAngle, float aEndAngle)
01108 {
01109    nsRect tRect( aX, aY, aWidth, aHeight);
01110    PMDrawArc( tRect, PR_FALSE, PR_FALSE, aStartAngle, aEndAngle);
01111    return NS_OK;
01112 }
01113 
01114 NS_IMETHODIMP nsRenderingContextOS2::FillArc( const nsRect& aRect,
01115                                          float aStartAngle, float aEndAngle)
01116 {
01117    nsRect tRect( aRect);
01118    PMDrawArc( tRect, PR_TRUE, PR_FALSE, aStartAngle, aEndAngle);
01119    return NS_OK;
01120 }
01121 
01122 NS_IMETHODIMP nsRenderingContextOS2::FillArc( nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
01123                                          float aStartAngle, float aEndAngle)
01124 {
01125    nsRect tRect( aX, aY, aWidth, aHeight);
01126    PMDrawArc( tRect, PR_TRUE, PR_FALSE, aStartAngle, aEndAngle);
01127    return NS_OK;
01128 }
01129 
01130 void nsRenderingContextOS2::PMDrawArc( nsRect &rect, PRBool bFilled, PRBool bFull,
01131                                        float start, float end)
01132 {
01133    // convert coords
01134    mTranMatrix->TransformCoord( &rect.x, &rect.y, &rect.width, &rect.height);
01135 
01136    RECTL rcl;
01137    mSurface->NS2PM_ININ (rect, rcl);
01138 
01139    // set arc params.
01140    long lWidth = rect.width / 2;
01141    long lHeight = rect.height / 2;
01142    ARCPARAMS arcparams = { lWidth, lHeight, 0, 0 };
01143    GFX (::GpiSetArcParams (mPS, &arcparams), FALSE);
01144 
01145    // move to center
01146    rcl.xLeft += lWidth;
01147    rcl.yBottom += lHeight;
01148    GFX (::GpiMove (mPS, (PPOINTL)&rcl), FALSE);
01149 
01150    if (bFilled)
01151       SetupFillColor ();
01152    else
01153       SetupLineColorAndStyle ();
01154 
01155 
01156    if (bFull)
01157    {
01158       long lOps = (bFilled) ? DRO_FILL : DRO_OUTLINE;
01159 
01160       // draw ellipse
01161       GFX (::GpiFullArc (mPS, lOps, MAKEFIXED(1,0)), GPI_ERROR);
01162    }
01163    else
01164    {
01165       FIXED StartAngle = (FIXED)(start * 65536.0) % MAKEFIXED (360, 0);
01166       FIXED EndAngle   = (FIXED)(end * 65536.0) % MAKEFIXED (360, 0);
01167       FIXED SweepAngle = EndAngle - StartAngle;
01168 
01169       if (SweepAngle < 0) SweepAngle += MAKEFIXED (360, 0);
01170 
01171       // draw an arc or a pie
01172       if (bFilled)
01173       {
01174          GFX (::GpiBeginArea (mPS, BA_NOBOUNDARY), FALSE);
01175          GFX (::GpiPartialArc (mPS, (PPOINTL)&rcl, MAKEFIXED(1,0), StartAngle, SweepAngle), GPI_ERROR);
01176          GFX (::GpiEndArea (mPS), GPI_ERROR);
01177       }
01178       else
01179       {
01180          // draw an invisible partialarc to get to the start of the arc.
01181          long lLineType = GFX (::GpiQueryLineType (mPS), LINETYPE_ERROR);
01182          GFX (::GpiSetLineType (mPS, LINETYPE_INVISIBLE), FALSE);
01183          GFX (::GpiPartialArc (mPS, (PPOINTL)&rcl, MAKEFIXED(1,0), StartAngle, MAKEFIXED (0,0)), GPI_ERROR);
01184          // now draw a real arc
01185          GFX (::GpiSetLineType (mPS, lLineType), FALSE);
01186          GFX (::GpiPartialArc (mPS, (PPOINTL)&rcl, MAKEFIXED(1,0), StartAngle, SweepAngle), GPI_ERROR);
01187       }
01188    }
01189 }
01190 
01191 NS_IMETHODIMP nsRenderingContextOS2 :: GetWidth(char ch, nscoord& aWidth)
01192 {
01193   char buf[1];
01194   buf[0] = ch;
01195   return GetWidth(buf, 1, aWidth);
01196 }
01197 
01198 NS_IMETHODIMP nsRenderingContextOS2 :: GetWidth(PRUnichar ch, nscoord &aWidth, PRInt32 *aFontID)
01199 {
01200   PRUnichar buf[1];
01201   buf[0] = ch;
01202   return GetWidth(buf, 1, aWidth, aFontID);
01203 }
01204 
01205 NS_IMETHODIMP nsRenderingContextOS2 :: GetWidth(const char* aString, nscoord& aWidth)
01206 {
01207   return GetWidth(aString, strlen(aString), aWidth);
01208 }
01209 
01210 NS_IMETHODIMP nsRenderingContextOS2 :: GetWidth(const char* aString,
01211                                                 PRUint32 aLength,
01212                                                 nscoord& aWidth)
01213 {
01214 
01215   if (nsnull != mFontMetrics)
01216   {
01217     // Check for the very common case of trying to get the width of a single
01218     // space.
01219     if ((1 == aLength) && (aString[0] == ' '))
01220     {
01221       return mFontMetrics->GetSpaceWidth(aWidth);
01222     }
01223 
01224     SetupFontAndColor();
01225     nscoord pxWidth = mCurrFontOS2->GetWidth(mPS, aString, aLength);
01226     aWidth = NSToCoordRound(float(pxWidth) * mP2T);
01227 
01228     return NS_OK;
01229   }
01230   else
01231     return NS_ERROR_FAILURE;
01232 }
01233 
01234 NS_IMETHODIMP nsRenderingContextOS2::GetWidth( const nsString &aString,
01235                                                nscoord &aWidth,
01236                                                PRInt32 *aFontID)
01237 {
01238    return GetWidth( aString.get(), aString.Length(), aWidth, aFontID);
01239 }
01240 
01241 struct GetWidthData {
01242   HPS                   mPS;      // IN
01243   nsDrawingSurfaceOS2*  mSurface; // IN
01244   nsFontOS2*            mFont;    // IN/OUT (running)
01245   LONG                  mWidth;   // IN/OUT (running, accumulated width so far)
01246 };
01247 
01248 static PRBool PR_CALLBACK
01249 do_GetWidth(const nsFontSwitch* aFontSwitch,
01250             const PRUnichar*    aSubstring,
01251             PRUint32            aSubstringLength,
01252             void*               aData)
01253 {
01254   nsFontOS2* font = aFontSwitch->mFont;
01255 
01256   GetWidthData* data = (GetWidthData*)aData;
01257   if (data->mFont != font) {
01258     // the desired font is not the current font in the PS
01259     data->mFont = font;
01260     data->mSurface->SelectFont(data->mFont);
01261   }
01262   data->mWidth += font->GetWidth(data->mPS, aSubstring, aSubstringLength);
01263   return PR_TRUE; // don't stop till the end
01264 }
01265 
01266 NS_IMETHODIMP nsRenderingContextOS2::GetWidth( const PRUnichar *aString,
01267                                                PRUint32 aLength,
01268                                                nscoord &aWidth,
01269                                                PRInt32 *aFontID)
01270 {
01271   if (!mFontMetrics)
01272     return NS_ERROR_FAILURE;
01273 
01274   SetupFontAndColor();
01275 
01276   nsFontMetricsOS2* metrics = (nsFontMetricsOS2*)mFontMetrics;
01277   GetWidthData data = {mPS, mSurface, mCurrFontOS2, 0};
01278 
01279   metrics->ResolveForwards(mPS, aString, aLength, do_GetWidth, &data);
01280   aWidth = NSToCoordRound(float(data.mWidth) * mP2T);
01281 
01282   if (data.mFont != mCurrFontOS2) {
01283     // If the font was changed along the way, restore our font
01284     mSurface->SelectFont(mCurrFontOS2);
01285   }
01286 
01287   if (aFontID)
01288     *aFontID = 0;
01289 
01290   return NS_OK;
01291 }
01292 
01293 NS_IMETHODIMP
01294 nsRenderingContextOS2::GetTextDimensions(const char*       aString,
01295                                          PRInt32           aLength,
01296                                          PRInt32           aAvailWidth,
01297                                          PRInt32*          aBreaks,
01298                                          PRInt32           aNumBreaks,
01299                                          nsTextDimensions& aDimensions,
01300                                          PRInt32&          aNumCharsFit,
01301                                          nsTextDimensions& aLastWordDimensions,
01302                                          PRInt32*          aFontID)
01303 {
01304   NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array");
01305 
01306   if (nsnull != mFontMetrics) {
01307     // Setup the font and foreground color
01308     SetupFontAndColor();
01309 
01310     // If we need to back up this state represents the last place we could
01311     // break. We can use this to avoid remeasuring text
01312     PRInt32 prevBreakState_BreakIndex = -1; // not known (hasn't been computed)
01313     nscoord prevBreakState_Width = 0; // accumulated width to this point
01314 
01315     // Initialize OUT parameters
01316     mFontMetrics->GetMaxAscent(aLastWordDimensions.ascent);
01317     mFontMetrics->GetMaxDescent(aLastWordDimensions.descent);
01318     aLastWordDimensions.width = -1;
01319     aNumCharsFit = 0;
01320 
01321     // Iterate each character in the string and determine which font to use
01322     nscoord width = 0;
01323     PRInt32 start = 0;
01324     nscoord aveCharWidth;
01325     mFontMetrics->GetAveCharWidth(aveCharWidth);
01326 
01327     while (start < aLength) {
01328       // Estimate how many characters will fit. Do that by diving the available
01329       // space by the average character width. Make sure the estimated number
01330       // of characters is at least 1
01331       PRInt32 estimatedNumChars = 0;
01332       if (aveCharWidth > 0) {
01333         estimatedNumChars = (aAvailWidth - width) / aveCharWidth;
01334       }
01335       if (estimatedNumChars < 1) {
01336         estimatedNumChars = 1;
01337       }
01338 
01339       // Find the nearest break offset
01340       PRInt32 estimatedBreakOffset = start + estimatedNumChars;
01341       PRInt32 breakIndex;
01342       nscoord numChars;
01343 
01344       // Find the nearest place to break that is less than or equal to
01345       // the estimated break offset
01346       if (aLength <= estimatedBreakOffset) {
01347         // All the characters should fit
01348         numChars = aLength - start;
01349         breakIndex = aNumBreaks - 1;
01350       } 
01351       else {
01352         breakIndex = prevBreakState_BreakIndex;
01353         while (((breakIndex + 1) < aNumBreaks) &&
01354                (aBreaks[breakIndex + 1] <= estimatedBreakOffset)) {
01355           ++breakIndex;
01356         }
01357         if (breakIndex == prevBreakState_BreakIndex) {
01358           ++breakIndex; // make sure we advanced past the previous break index
01359         }
01360         numChars = aBreaks[breakIndex] - start;
01361       }
01362 
01363       // Measure the text
01364       nscoord pxWidth, twWidth = 0;
01365       if ((1 == numChars) && (aString[start] == ' ')) {
01366         mFontMetrics->GetSpaceWidth(twWidth);
01367       } 
01368       else if (numChars > 0) {
01369         pxWidth = mCurrFontOS2->GetWidth(mPS, &aString[start], numChars);
01370         twWidth = NSToCoordRound(float(pxWidth) * mP2T);
01371       }
01372 
01373       // See if the text fits
01374       PRBool  textFits = (twWidth + width) <= aAvailWidth;
01375 
01376       // If the text fits then update the width and the number of
01377       // characters that fit
01378       if (textFits) {
01379         aNumCharsFit += numChars;
01380         width += twWidth;
01381         start += numChars;
01382 
01383         // This is a good spot to back up to if we need to so remember
01384         // this state
01385         prevBreakState_BreakIndex = breakIndex;
01386         prevBreakState_Width = width;
01387       }
01388       else {
01389         // See if we can just back up to the previous saved state and not
01390         // have to measure any text
01391         if (prevBreakState_BreakIndex > 0) {
01392           // If the previous break index is just before the current break index
01393           // then we can use it
01394           if (prevBreakState_BreakIndex == (breakIndex - 1)) {
01395             aNumCharsFit = aBreaks[prevBreakState_BreakIndex];
01396             width = prevBreakState_Width;
01397             break;
01398           }
01399         }
01400 
01401         // We can't just revert to the previous break state
01402         if (0 == breakIndex) {
01403           // There's no place to back up to, so even though the text doesn't fit
01404           // return it anyway
01405           aNumCharsFit += numChars;
01406           width += twWidth;
01407           break;
01408         }
01409 
01410         // Repeatedly back up until we get to where the text fits or we're all
01411         // the way back to the first word
01412         width += twWidth;
01413         while ((breakIndex >= 1) && (width > aAvailWidth)) {
01414           twWidth = 0;
01415           start = aBreaks[breakIndex - 1];
01416           numChars = aBreaks[breakIndex] - start;
01417           
01418           if ((1 == numChars) && (aString[start] == ' ')) {
01419             mFontMetrics->GetSpaceWidth(twWidth);
01420           } 
01421           else if (numChars > 0) {
01422             pxWidth = mCurrFontOS2->GetWidth(mPS, &aString[start], numChars);
01423             twWidth = NSToCoordRound(float(pxWidth) * mP2T);
01424           }
01425 
01426           width -= twWidth;
01427           aNumCharsFit = start;
01428           breakIndex--;
01429         }
01430         break;
01431       }
01432     }
01433 
01434     aDimensions.width = width;
01435     mFontMetrics->GetMaxAscent(aDimensions.ascent);
01436     mFontMetrics->GetMaxDescent(aDimensions.descent);
01437 
01438     return NS_OK;
01439   }
01440 
01441   return NS_ERROR_FAILURE;
01442 }
01443 
01444 struct BreakGetTextDimensionsData {
01445   HPS         mPS;                // IN
01446   nsDrawingSurfaceOS2*  mSurface; // IN
01447   nsFontOS2*  mFont;              // IN/OUT (running)
01448   float       mP2T;               // IN
01449   PRInt32     mAvailWidth;        // IN
01450   PRInt32*    mBreaks;            // IN
01451   PRInt32     mNumBreaks;         // IN
01452   nscoord     mSpaceWidth;        // IN
01453   nscoord     mAveCharWidth;      // IN
01454   PRInt32     mEstimatedNumChars; // IN (running -- to handle the edge case of one word)
01455 
01456   PRInt32     mNumCharsFit;  // IN/OUT -- accumulated number of chars that fit so far
01457   nscoord     mWidth;        // IN/OUT -- accumulated width so far
01458 
01459   // If we need to back up, this state represents the last place
01460   // we could break. We can use this to avoid remeasuring text
01461   PRInt32 mPrevBreakState_BreakIndex; // IN/OUT, initialized as -1, i.e., not yet computed
01462   nscoord mPrevBreakState_Width;      // IN/OUT, initialized as  0
01463 
01464   // Remember the fonts that we use so that we can deal with
01465   // line-breaking in-between fonts later. mOffsets[0] is also used
01466   // to initialize the current offset from where to start measuring
01467   nsVoidArray* mFonts;   // IN/OUT
01468   nsVoidArray* mOffsets; // IN/OUT
01469 };
01470 
01471 static PRBool PR_CALLBACK
01472 do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
01473                           const PRUnichar*    aSubstring,
01474                           PRUint32            aSubstringLength,
01475                           void*               aData)
01476 {
01477   nsFontOS2* font = aFontSwitch->mFont;
01478 
01479   // Make sure the font is selected
01480   BreakGetTextDimensionsData* data = (BreakGetTextDimensionsData*)aData;
01481   if (data->mFont != font) {
01482     // the desired font is not the current font in the PS
01483     data->mFont = font;
01484     data->mSurface->SelectFont(data->mFont);
01485   }
01486 
01487    // set mMaxAscent & mMaxDescent if not already set in nsFontOS2 struct
01488   if (font->mMaxAscent == 0)
01489   {
01490     FONTMETRICS fm;
01491     GFX (::GpiQueryFontMetrics(data->mPS, sizeof (fm), &fm), FALSE);
01492     
01493     font->mMaxAscent  = NSToCoordRound( (fm.lMaxAscender-1) * data->mP2T );
01494     font->mMaxDescent = NSToCoordRound( (fm.lMaxDescender+1) * data->mP2T );
01495   }
01496 
01497   // Our current state relative to the _full_ string...
01498   // This allows emulation of the previous code...
01499   const PRUnichar* pstr = (const PRUnichar*)data->mOffsets->ElementAt(0);
01500   PRInt32 numCharsFit = data->mNumCharsFit;
01501   nscoord width = data->mWidth;
01502   PRInt32 start = (PRInt32)(aSubstring - pstr);
01503   PRInt32 end = start + aSubstringLength;
01504   PRBool allDone = PR_FALSE;
01505 
01506   while (start < end) {
01507     // Estimate how many characters will fit. Do that by dividing the
01508     // available space by the average character width
01509     PRInt32 estimatedNumChars = data->mEstimatedNumChars;
01510     if (!estimatedNumChars && data->mAveCharWidth > 0) {
01511       estimatedNumChars = (data->mAvailWidth - width) / data->mAveCharWidth;
01512     }
01513     // Make sure the estimated number of characters is at least 1
01514     if (estimatedNumChars < 1) {
01515       estimatedNumChars = 1;
01516     }
01517 
01518     // Find the nearest break offset
01519     PRInt32 estimatedBreakOffset = start + estimatedNumChars;
01520     PRInt32 breakIndex = -1; // not yet computed
01521     PRBool  inMiddleOfSegment = PR_FALSE;
01522     nscoord numChars;
01523 
01524     // Avoid scanning the break array in the case where we think all
01525     // the text should fit
01526     if (end <= estimatedBreakOffset) {
01527       // Everything should fit
01528       numChars = end - start;
01529     }
01530     else {
01531       // Find the nearest place to break that is less than or equal to
01532       // the estimated break offset
01533       breakIndex = data->mPrevBreakState_BreakIndex;
01534       while (breakIndex + 1 < data->mNumBreaks &&
01535              data->mBreaks[breakIndex + 1] <= estimatedBreakOffset) {
01536         ++breakIndex;
01537       }
01538 
01539       if (breakIndex == -1)
01540         breakIndex = 0;
01541 
01542       // We found a place to break that is before the estimated break
01543       // offset. Where we break depends on whether the text crosses a
01544       // segment boundary
01545       if (start < data->mBreaks[breakIndex]) {
01546         // The text crosses at least one segment boundary so measure to the
01547         // break point just before the estimated break offset
01548         numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
01549       } 
01550       else {
01551         // See whether there is another segment boundary between this one
01552         // and the end of the text
01553         if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < end)) {
01554           ++breakIndex;
01555           numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
01556         }
01557         else {
01558           NS_ASSERTION(end != data->mBreaks[breakIndex], "don't expect to be at segment boundary");
01559 
01560           // The text is all within the same segment
01561           numChars = end - start;
01562 
01563           // Remember we're in the middle of a segment and not between
01564           // two segments
01565           inMiddleOfSegment = PR_TRUE;
01566         }
01567       }
01568     }
01569 
01570     // Measure the text
01571     nscoord twWidth, pxWidth;
01572     if ((1 == numChars) && (pstr[start] == ' ')) {
01573       twWidth = data->mSpaceWidth;
01574     }
01575     else {
01576       pxWidth = font->GetWidth(data->mPS, &pstr[start], numChars);
01577       twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
01578     }
01579 
01580     // See if the text fits
01581     PRBool textFits = (twWidth + width) <= data->mAvailWidth;
01582 
01583     // If the text fits then update the width and the number of
01584     // characters that fit
01585     if (textFits) {
01586       numCharsFit += numChars;
01587       width += twWidth;
01588 
01589       // If we computed the break index and we're not in the middle
01590       // of a segment then this is a spot that we can back up to if
01591       // we need to, so remember this state
01592       if ((breakIndex != -1) && !inMiddleOfSegment) {
01593         data->mPrevBreakState_BreakIndex = breakIndex;
01594         data->mPrevBreakState_Width = width;
01595       }
01596     }
01597     else {
01598       // The text didn't fit. If we're out of room then we're all done
01599       allDone = PR_TRUE;
01600 
01601       // See if we can just back up to the previous saved state and not
01602       // have to measure any text
01603       if (data->mPrevBreakState_BreakIndex != -1) {
01604         PRBool canBackup;
01605 
01606         // If we're in the middle of a word then the break index
01607         // must be the same if we can use it. If we're at a segment
01608         // boundary, then if the saved state is for the previous
01609         // break index then we can use it
01610         if (inMiddleOfSegment) {
01611           canBackup = data->mPrevBreakState_BreakIndex == breakIndex;
01612         } else {
01613           canBackup = data->mPrevBreakState_BreakIndex == (breakIndex - 1);
01614         }
01615 
01616         if (canBackup) {
01617           numCharsFit = data->mBreaks[data->mPrevBreakState_BreakIndex];
01618           width = data->mPrevBreakState_Width;
01619           break;
01620         }
01621       }
01622 
01623       // We can't just revert to the previous break state. Find the break
01624       // index just before the end of the text
01625       end = start + numChars;
01626       breakIndex = 0;
01627       if (data->mBreaks[breakIndex] < end) {
01628         while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < end)) {
01629           ++breakIndex;
01630         }
01631       }
01632 
01633       if ((0 == breakIndex) && (end <= data->mBreaks[0])) {
01634         // There's no place to back up to, so even though the text doesn't fit
01635         // return it anyway
01636         numCharsFit += numChars;
01637         width += twWidth;
01638 
01639         // Edge case of one word: it could be that we just measured a fragment of the
01640         // first word and its remainder involves other fonts, so we want to keep going
01641         // until we at least measure the entire first word
01642         if (numCharsFit < data->mBreaks[0]) {
01643           allDone = PR_FALSE;
01644           // From now on we don't care anymore what is the _real_ estimated
01645           // number of characters that fits. Rather, we have no where to break
01646           // and have to measure one word fully, but the real estimate is less
01647           // than that one word. However, since the other bits of code rely on
01648           // what is in "data->mEstimatedNumChars", we want to override
01649           // "data->mEstimatedNumChars" and pass in what _has_ to be measured
01650           // so that it is transparent to the other bits that depend on it.
01651           data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit;
01652           start += numChars;
01653         }
01654 
01655         break;
01656       }
01657 
01658       // Repeatedly back up until we get to where the text fits or we're
01659       // all the way back to the first word
01660       width += twWidth;
01661       while ((breakIndex >= 0) && (width > data->mAvailWidth)) {
01662         start = data->mBreaks[breakIndex];
01663         numChars = end - start;
01664         numCharsFit = start;
01665         if ((1 == numChars) && (pstr[start] == ' ')) {
01666           width -= data->mSpaceWidth;
01667         }
01668         else if (pstr + start >= aSubstring) {
01669           // The entire fragment to chop is within the current font.
01670           pxWidth = font->GetWidth(data->mPS, &pstr[start], numChars);
01671           width -= NSToCoordRound(float(pxWidth) * data->mP2T);
01672         }
01673         else {
01674           // The fragment that we want to chop extends back into previous fonts.
01675           // We need to reverse into previous fonts. Fortunately,
01676           // data->mFonts[] and data->mOffsets[] tell us which fonts are used
01677           // and when. 
01678           end = data->mNumCharsFit; // same as aSubstring - pstr
01679           data->mNumCharsFit = numCharsFit; // has got shorter...
01680           PRInt32 k = data->mFonts->Count() - 1;
01681           for ( ; k >= 0 && start < end; --k, end -= numChars) {
01682             font = (nsFontOS2*)data->mFonts->ElementAt(k);
01683             const PRUnichar* ps = (const PRUnichar*)data->mOffsets->ElementAt(k);
01684             if (ps < pstr + start)
01685               ps = pstr + start;
01686 
01687             numChars = pstr + end - ps;
01688             NS_ASSERTION(numChars > 0, "empty string");
01689 
01690             data->mFont = font;
01691             data->mSurface->SelectFont(data->mFont);
01692             pxWidth = font->GetWidth(data->mPS, ps, numChars);
01693             data->mWidth -= NSToCoordRound(float(pxWidth) * data->mP2T);
01694 
01695             // By construction, mFonts[k] is the last font, and
01696             // mOffsets[k+1] is the last offset.
01697             data->mFonts->RemoveElementAt(k);
01698             data->mOffsets->RemoveElementAt(k+1);
01699           }
01700 
01701           // We are done, update the data now because we won't do it later.
01702           // The |if (data->mNumCharsFit != numCharsFit)| won't apply below
01703           data->mFonts->AppendElement(font);
01704           data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
01705           break;
01706         }
01707 
01708         --breakIndex;
01709         end = start;
01710       }
01711     }
01712 
01713     start += numChars;
01714   }
01715 
01716 #ifdef DEBUG_rbs
01717   NS_ASSERTION(allDone || start == end, "internal error");
01718   NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error");
01719 #endif /* DEBUG_rbs */
01720 
01721   if (data->mNumCharsFit != numCharsFit) {
01722     // some text was actually retained
01723     data->mWidth = width;
01724     data->mNumCharsFit = numCharsFit;
01725     data->mFonts->AppendElement(font);
01726     data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
01727   }
01728 
01729   if (allDone) {
01730     // stop now
01731     return PR_FALSE;
01732   }
01733 
01734   return PR_TRUE; // don't stop if we still need to measure more characters
01735 }
01736 
01737 NS_IMETHODIMP
01738 nsRenderingContextOS2::GetTextDimensions(const PRUnichar*  aString,
01739                                          PRInt32           aLength,
01740                                          PRInt32           aAvailWidth,
01741                                          PRInt32*          aBreaks,
01742                                          PRInt32           aNumBreaks,
01743                                          nsTextDimensions& aDimensions,
01744                                          PRInt32&          aNumCharsFit,
01745                                          nsTextDimensions& aLastWordDimensions,
01746                                          PRInt32*          aFontID)
01747 {
01748   if (!mFontMetrics)
01749     return NS_ERROR_FAILURE;
01750 
01751   SetupFontAndColor();
01752 
01753   nsFontMetricsOS2* metrics = (nsFontMetricsOS2*)mFontMetrics;
01754 
01755   nscoord spaceWidth, aveCharWidth;
01756   metrics->GetSpaceWidth(spaceWidth);
01757   metrics->GetAveCharWidth(aveCharWidth);
01758 
01759   // Note: aBreaks[] is supplied to us so that the first word is located
01760   // at aString[0 .. aBreaks[0]-1] and more generally, the k-th word is
01761   // located at aString[aBreaks[k-1] .. aBreaks[k]-1]. Whitespace can
01762   // be included and each of them counts as a word in its own right.
01763 
01764   // Upon completion of glyph resolution, characters that can be
01765   // represented with fonts[i] are at offsets[i] .. offsets[i+1]-1
01766 
01767   nsAutoVoidArray fonts, offsets;
01768   offsets.AppendElement((void*)aString);
01769 
01770   BreakGetTextDimensionsData data = {mPS, mSurface, mCurrFontOS2, mP2T,
01771                                      aAvailWidth, aBreaks, aNumBreaks,
01772                                      spaceWidth, aveCharWidth, 0, 0, 0, -1, 0,
01773                                      &fonts, &offsets};
01774 
01775   metrics->ResolveForwards(mPS, aString, aLength, do_BreakGetTextDimensions, &data);
01776 
01777   if (data.mFont != mCurrFontOS2) {
01778     // If the font was changed along the way, restore our font
01779     mSurface->SelectFont(mCurrFontOS2);
01780   }
01781 
01782   if (aFontID)
01783     *aFontID = 0;
01784 
01785   aNumCharsFit = data.mNumCharsFit;
01786   aDimensions.width = data.mWidth;
01787 
01789   // Post-processing for the ascent and descent:
01790   //
01791   // The width of the last word is included in the final width, but its
01792   // ascent and descent are kept aside for the moment. The problem is that
01793   // line-breaking may occur _before_ the last word, and we don't want its
01794   // ascent and descent to interfere. We can re-measure the last word and
01795   // substract its width later. However, we need a special care for the ascent
01796   // and descent at the break-point. The idea is to keep the ascent and descent
01797   // of the last word separate, and let layout consider them later when it has
01798   // determined that line-breaking doesn't occur before the last word.
01799   //
01800   // Therefore, there are two things to do:
01801   // 1. Determine the ascent and descent up to where line-breaking may occur.
01802   // 2. Determine the ascent and descent of the remainder.
01803   //    For efficiency however, it is okay to bail out early if there is only
01804   //    one font (in this case, the height of the last word has no special
01805   //    effect on the total height).
01806 
01807   // aLastWordDimensions.width should be set to -1 to reply that we don't
01808   // know the width of the last word since we measure multiple words
01809   aLastWordDimensions.Clear();
01810   aLastWordDimensions.width = -1;
01811 
01812   PRInt32 count = fonts.Count();
01813   if (!count)
01814     return NS_OK;
01815   nsFontOS2* font = (nsFontOS2*)fonts[0];
01816   NS_ASSERTION(font, "internal error in do_BreakGetTextDimensions");
01817   aDimensions.ascent = font->mMaxAscent;
01818   aDimensions.descent = font->mMaxDescent;
01819 
01820   // fast path - normal case, quick return if there is only one font
01821   if (count == 1)
01822     return NS_OK;
01823 
01824   // get the last break index.
01825   // If there is only one word, we end up with lastBreakIndex = 0. We don't
01826   // need to worry about aLastWordDimensions in this case too. But if we didn't
01827   // return earlier, it would mean that the unique word needs several fonts
01828   // and we will still have to loop over the fonts to return the final height
01829   PRInt32 lastBreakIndex = 0;
01830   while (aBreaks[lastBreakIndex] < aNumCharsFit)
01831     ++lastBreakIndex;
01832 
01833   const PRUnichar* lastWord = (lastBreakIndex > 0) 
01834     ? aString + aBreaks[lastBreakIndex-1]
01835     : aString + aNumCharsFit; // let it point outside to play nice with the loop
01836 
01837   // now get the desired ascent and descent information... this is however
01838   // a very fast loop of the order of the number of additional fonts
01839 
01840   PRInt32 currFont = 0;
01841   const PRUnichar* pstr = aString;
01842   const PRUnichar* last = aString + aNumCharsFit;
01843 
01844   while (pstr < last) {
01845     font = (nsFontOS2*)fonts[currFont];
01846     PRUnichar* nextOffset = (PRUnichar*)offsets[++currFont]; 
01847 
01848     // For consistent word-wrapping, we are going to handle the whitespace
01849     // character with special care because a whitespace character can come
01850     // from a font different from that of the previous word. If 'x', 'y', 'z',
01851     // are Unicode points that require different fonts, we want 'xyz <br>'
01852     // and 'xyz<br>' to have the same height because it gives a more stable
01853     // rendering, especially when the window is resized at the edge of the word.
01854     // If we don't do this, a 'tall' trailing whitespace, i.e., if the whitespace
01855     // happens to come from a font with a bigger ascent and/or descent than all
01856     // current fonts on the line, this can cause the next lines to be shifted
01857     // down when the window is slowly resized to fit that whitespace.
01858     if (*pstr == ' ') {
01859       // skip pass the whitespace to ignore the height that it may contribute
01860       ++pstr;
01861       // get out if we reached the end
01862       if (pstr == last) {
01863         break;
01864       }
01865       // switch to the next font if we just passed the current font 
01866       if (pstr == nextOffset) {
01867         font = (nsFontOS2*)fonts[currFont];
01868         nextOffset = (PRUnichar*)offsets[++currFont];
01869       } 
01870     }
01871 
01872     // see if the last word intersects with the current font
01873     // (we are testing for 'nextOffset-1 >= lastWord' since the
01874     // current font ends at nextOffset-1)
01875     if (nextOffset > lastWord) {
01876       if (aLastWordDimensions.ascent < font->mMaxAscent) {
01877         aLastWordDimensions.ascent = font->mMaxAscent;
01878       }
01879       if (aLastWordDimensions.descent < font->mMaxDescent) {
01880         aLastWordDimensions.descent = font->mMaxDescent;
01881       }
01882     }
01883 
01884     // see if we have not reached the last word yet
01885     if (pstr < lastWord) {
01886       if (aDimensions.ascent < font->mMaxAscent) {
01887         aDimensions.ascent = font->mMaxAscent;
01888       }
01889       if (aDimensions.descent < font->mMaxDescent) {
01890         aDimensions.descent = font->mMaxDescent;
01891       }
01892     }
01893 
01894     // advance to where the next font starts
01895     pstr = nextOffset;
01896   }
01897 
01898   return NS_OK;
01899 }
01900 
01901 NS_IMETHODIMP
01902 nsRenderingContextOS2::GetTextDimensions(const char*       aString,
01903                                          PRUint32          aLength,
01904                                          nsTextDimensions& aDimensions)
01905 {
01906   GetWidth(aString, aLength, aDimensions.width);
01907   if (mFontMetrics)
01908   {
01909     mFontMetrics->GetMaxAscent(aDimensions.ascent);
01910     mFontMetrics->GetMaxDescent(aDimensions.descent);
01911   }
01912   return NS_OK;
01913 }
01914 
01915 struct GetTextDimensionsData {
01916   HPS                   mPS;      // IN
01917   float                 mP2T;     // IN
01918   nsDrawingSurfaceOS2*  mSurface; // IN
01919   nsFontOS2*            mFont;    // IN/OUT (running)
01920   LONG                  mWidth;   // IN/OUT (running)
01921   nscoord               mAscent;  // IN/OUT (running)
01922   nscoord               mDescent; // IN/OUT (running)
01923 };
01924 
01925 static PRBool PR_CALLBACK
01926 do_GetTextDimensions(const nsFontSwitch* aFontSwitch,
01927                      const PRUnichar*    aSubstring,
01928                      PRUint32            aSubstringLength,
01929                      void*               aData)
01930 {
01931   nsFontOS2* font = aFontSwitch->mFont;
01932   
01933   GetTextDimensionsData* data = (GetTextDimensionsData*)aData;
01934   if (data->mFont != font) {
01935     // the desired font is not the current font in the PS
01936     data->mFont = font;
01937     data->mSurface->SelectFont(data->mFont);
01938   }
01939 
01940   data->mWidth += font->GetWidth(data->mPS, aSubstring, aSubstringLength);
01941 
01942    // set mMaxAscent & mMaxDescent if not already set in nsFontOS2 struct
01943   if (font->mMaxAscent == 0) {
01944     FONTMETRICS fm;
01945     GFX (::GpiQueryFontMetrics(data->mPS, sizeof (fm), &fm), FALSE);
01946     font->mMaxAscent  = NSToCoordRound( (fm.lMaxAscender-1) * data->mP2T );
01947     font->mMaxDescent = NSToCoordRound( (fm.lMaxDescender+1) * data->mP2T );
01948   }
01949 
01950   if (data->mAscent < font->mMaxAscent) {
01951     data->mAscent = font->mMaxAscent;
01952   }
01953   if (data->mDescent < font->mMaxDescent) {
01954     data->mDescent = font->mMaxDescent;
01955   }
01956 
01957   return PR_TRUE; // don't stop till the end
01958 }
01959 
01960 NS_IMETHODIMP
01961 nsRenderingContextOS2::GetTextDimensions(const PRUnichar*  aString,
01962                                          PRUint32          aLength,
01963                                          nsTextDimensions& aDimensions,
01964                                          PRInt32*          aFontID)
01965 {
01966   aDimensions.Clear();
01967 
01968   if (!mFontMetrics)
01969     return NS_ERROR_FAILURE;
01970 
01971   SetupFontAndColor();
01972 
01973   nsFontMetricsOS2* metrics = (nsFontMetricsOS2*)mFontMetrics;
01974   GetTextDimensionsData data = {mPS, mP2T, mSurface, mCurrFontOS2, 0, 0, 0};
01975 
01976   metrics->ResolveForwards(mPS, aString, aLength, do_GetTextDimensions, &data);
01977   aDimensions.width = NSToCoordRound(float(data.mWidth) * mP2T);
01978   aDimensions.ascent = data.mAscent;
01979   aDimensions.descent = data.mDescent;
01980 
01981   if (data.mFont != mCurrFontOS2) {
01982     // If the font was changed along the way, restore our font
01983     mSurface->SelectFont(mCurrFontOS2);
01984   }
01985 
01986   if (aFontID) *aFontID = 0;
01987 
01988   return NS_OK;
01989 }
01990 
01991 NS_IMETHODIMP nsRenderingContextOS2 :: DrawString(const char *aString, PRUint32 aLength,
01992                                                   nscoord aX, nscoord aY,
01993                                                   const nscoord* aSpacing)
01994 {
01995   NS_PRECONDITION(mFontMetrics,"Something is wrong somewhere");
01996 
01997   PRInt32 x = aX;
01998   PRInt32 y = aY;
01999 
02000   SetupFontAndColor();
02001 
02002   INT dxMem[500];
02003   INT* dx0 = NULL;
02004   if (aSpacing) {
02005     dx0 = dxMem;
02006     if (aLength > 500) {
02007       dx0 = new INT[aLength];
02008     }
02009     mTranMatrix->ScaleXCoords(aSpacing, aLength, dx0);
02010   }
02011   mTranMatrix->TransformCoord(&x, &y);
02012 
02013   mCurrFontOS2->DrawString(mPS, mSurface, x, y, aString, aLength, dx0);
02014 
02015   if (dx0 && (dx0 != dxMem)) {
02016     delete [] dx0;
02017   }
02018 
02019   return NS_OK;
02020 }
02021 
02022 struct DrawStringData {
02023   HPS                   mPS;        // IN
02024   nsDrawingSurfaceOS2*  mSurface;   // IN
02025   nsFontOS2*            mFont;      // IN/OUT (running)
02026   nsTransform2D*        mTranMatrix;// IN
02027   nscoord               mX;         // IN/OUT (running)
02028   nscoord               mY;         // IN
02029   const nscoord*        mSpacing;   // IN
02030   nscoord               mMaxLength; // IN (length of the full string)
02031   nscoord               mLength;    // IN/OUT (running, current length already rendered)
02032 };
02033 
02034 static PRBool PR_CALLBACK
02035 do_DrawString(const nsFontSwitch* aFontSwitch,
02036               const PRUnichar*    aSubstring,
02037               PRUint32            aSubstringLength,
02038               void*               aData)
02039 {
02040   nsFontOS2* font = aFontSwitch->mFont;
02041 
02042   PRInt32 x, y;
02043   DrawStringData* data = (DrawStringData*)aData;
02044   if (data->mFont != font) {
02045     // the desired font is not the current font in the PS
02046     data->mFont = font;
02047     data->mSurface->SelectFont(data->mFont);
02048   }
02049 
02050   data->mLength += aSubstringLength;
02051   if (data->mSpacing) {
02052     // XXX Fix path to use a twips transform in the DC and use the
02053     // spacing values directly and let windows deal with the sub-pixel
02054     // positioning.
02055 
02056     // Slow, but accurate rendering
02057     const PRUnichar* str = aSubstring;
02058     const PRUnichar* end = aSubstring + aSubstringLength;
02059     while (str < end) {
02060       // XXX can shave some cycles by inlining a version of transform
02061       // coord where y is constant and transformed once
02062       x = data->mX;
02063       y = data->mY;
02064       data->mTranMatrix->TransformCoord(&x, &y);
02065       if (IS_HIGH_SURROGATE(*str) && 
02066           ((str+1)<end) && 
02067           IS_LOW_SURROGATE(*(str+1))) 
02068       {
02069         // special case for surrogate pair
02070         font->DrawString(data->mPS, data->mSurface, x, y, str, 2);
02071         // we need to advance data->mX and str twice
02072         data->mX += *data->mSpacing++;
02073         ++str;
02074       } else {
02075         font->DrawString(data->mPS, data->mSurface, x, y, str, 1);
02076       }
02077       data->mX += *data->mSpacing++;
02078       ++str;
02079     }
02080   }
02081   else {
02082     font->DrawString(data->mPS, data->mSurface, data->mX, data->mY, aSubstring,
02083                      aSubstringLength);
02084     // be ready if there is more to come
02085     if (data->mLength < data->mMaxLength) {
02086       data->mX += font->GetWidth(data->mPS, aSubstring, aSubstringLength);
02087     }
02088   }
02089   return PR_TRUE; // don't stop till the end
02090 }
02091 
02092 NS_IMETHODIMP nsRenderingContextOS2 :: DrawString(const PRUnichar *aString, PRUint32 aLength,
02093                                                   nscoord aX, nscoord aY,
02094                                                   PRInt32 aFontID,
02095                                                   const nscoord* aSpacing)
02096 {
02097   if (!mFontMetrics)
02098     return NS_ERROR_FAILURE;
02099 
02100   SetupFontAndColor();
02101 
02102   nsFontMetricsOS2* metrics = (nsFontMetricsOS2*)mFontMetrics;
02103   DrawStringData data = {mPS, mSurface, mCurrFontOS2, mTranMatrix, aX, aY,
02104                          aSpacing, aLength, 0};
02105   if (!aSpacing) { // @see do_DrawString for the spacing case
02106     mTranMatrix->TransformCoord(&data.mX, &data.mY);
02107   }
02108 
02109   if (mRightToLeftText) {
02110     metrics->ResolveBackwards(mPS, aString, aLength, do_DrawString, &data);
02111   }
02112   else
02113   {
02114     metrics->ResolveForwards(mPS, aString, aLength, do_DrawString, &data);
02115   }
02116 
02117   if (data.mFont != mCurrFontOS2) {
02118     // If the font was changed along the way, restore our font
02119     mSurface->SelectFont(mCurrFontOS2);
02120   }
02121 
02122   return NS_OK;
02123 }
02124 
02125 NS_IMETHODIMP nsRenderingContextOS2 :: DrawString(const nsString& aString,
02126                                                   nscoord aX, nscoord aY,
02127                                                   PRInt32 aFontID,
02128                                                   const nscoord* aSpacing)
02129 {
02130   return DrawString(aString.get(), aString.Length(), aX, aY, aFontID, aSpacing);
02131 }
02132 
02133 #ifdef MOZ_MATHML
02134 NS_IMETHODIMP 
02135 nsRenderingContextOS2::GetBoundingMetrics(const char*        aString,
02136                                           PRUint32           aLength,
02137                                           nsBoundingMetrics& aBoundingMetrics)
02138 {
02139 #if 0
02140   NS_PRECONDITION(mFontMetrics,"Something is wrong somewhere");
02141 
02142   aBoundingMetrics.Clear();
02143   if (!mFontMetrics) return NS_ERROR_FAILURE;
02144 
02145   SetupFontAndColor();
02146 
02147   // set glyph transform matrix to identity
02148   MAT2 mat2;
02149   FIXED zero, one;
02150   zero.fract = 0; one.fract = 0;
02151   zero.value = 0; one.value = 1; 
02152   mat2.eM12 = mat2.eM21 = zero; 
02153   mat2.eM11 = mat2.eM22 = one; 
02154 
02155   // measure the string
02156   nscoord descent;
02157   GLYPHMETRICS gm;
02158   DWORD len = GetGlyphOutline(mDC, aString[0], GGO_METRICS, &gm, 0, nsnull, &mat2);
02159   if (GDI_ERROR == len) {
02160     return NS_ERROR_UNEXPECTED;
02161   }
02162   // flip sign of descent for cross-platform compatibility
02163   descent = -(nscoord(gm.gmptGlyphOrigin.y) - nscoord(gm.gmBlackBoxY));
02164   aBoundingMetrics.leftBearing = gm.gmptGlyphOrigin.x;
02165   aBoundingMetrics.rightBearing = gm.gmptGlyphOrigin.x + gm.gmBlackBoxX;
02166   aBoundingMetrics.ascent = gm.gmptGlyphOrigin.y;
02167   aBoundingMetrics.descent = gm.gmptGlyphOrigin.y - gm.gmBlackBoxY;
02168   aBoundingMetrics.width = gm.gmCellIncX;
02169 
02170   if (1 < aLength) {
02171     // loop over each glyph to get the ascent and descent
02172     PRUint32 i;
02173     for (i = 1; i < aLength; i++) {
02174       len = GetGlyphOutline(mDC, aString[i], GGO_METRICS, &gm, 0, nsnull, &mat2);
02175       if (GDI_ERROR == len) {
02176         return NS_ERROR_UNEXPECTED;
02177       }
02178       // flip sign of descent for cross-platform compatibility
02179       descent = -(nscoord(gm.gmptGlyphOrigin.y) - nscoord(gm.gmBlackBoxY));
02180       if (aBoundingMetrics.ascent < gm.gmptGlyphOrigin.y)
02181         aBoundingMetrics.ascent = gm.gmptGlyphOrigin.y;
02182       if (aBoundingMetrics.descent > descent)
02183         aBoundingMetrics.descent = descent;
02184     }
02185     // get the final rightBearing and width. Possible kerning is taken into account.
02186     SIZE size;
02187     ::GetTextExtentPoint32(mDC, aString, aLength, &size);
02188     aBoundingMetrics.width = size.cx;
02189     aBoundingMetrics.rightBearing = size.cx - gm.gmCellIncX + gm.gmBlackBoxX;
02190   }
02191 
02192   // convert to app units
02193   aBoundingMetrics.leftBearing = NSToCoordRound(float(aBoundingMetrics.leftBearing) * mP2T);
02194   aBoundingMetrics.rightBearing = NSToCoordRound(float(aBoundingMetrics.rightBearing) * mP2T);
02195   aBoundingMetrics.width = NSToCoordRound(float(aBoundingMetrics.width) * mP2T);
02196   aBoundingMetrics.ascent = NSToCoordRound(float(aBoundingMetrics.ascent) * mP2T);
02197   aBoundingMetrics.descent = NSToCoordRound(float(aBoundingMetrics.descent) * mP2T);
02198 
02199   return NS_OK;
02200 #endif
02201   return NS_ERROR_FAILURE;
02202 }
02203 
02204 #if 0
02205 struct GetBoundingMetricsData {
02206   HDC                mDC;              // IN
02207   HFONT              mFont;            // IN/OUT (running)
02208   nsBoundingMetrics* mBoundingMetrics; // IN/OUT (running)
02209   PRBool             mFirstTime;       // IN/OUT (set once)
02210   nsresult           mStatus;          // OUT
02211 };
02212 
02213 static PRBool PR_CALLBACK
02214 do_GetBoundingMetrics(const nsFontSwitch* aFontSwitch,
02215                       const PRUnichar*    aSubstring,
02216                       PRUint32            aSubstringLength,
02217                       void*               aData)
02218 {
02219   nsFontWin* fontWin = aFontSwitch->mFontWin;
02220 
02221   GetBoundingMetricsData* data = (GetBoundingMetricsData*)aData;
02222   if (data->mFont != fontWin->mFont) {
02223     data->mFont = fontWin->mFont;
02224     ::SelectObject(data->mDC, data->mFont);
02225   }
02226 
02227   nsBoundingMetrics rawbm;
02228   data->mStatus = fontWin->GetBoundingMetrics(data->mDC, aSubstring, aSubstringLength, rawbm);
02229   if (NS_FAILED(data->mStatus)) {
02230     return PR_FALSE; // stop now
02231   }
02232 
02233   if (data->mFirstTime) {
02234     data->mFirstTime = PR_FALSE;
02235     *data->mBoundingMetrics = rawbm;
02236   }
02237   else {
02238     *data->mBoundingMetrics += rawbm;
02239   }
02240 
02241   return PR_TRUE; // don't stop till the end
02242 }
02243 #endif
02244 
02245 NS_IMETHODIMP
02246 nsRenderingContextOS2::GetBoundingMetrics(const PRUnichar*   aString,
02247                                           PRUint32           aLength,
02248                                           nsBoundingMetrics& aBoundingMetrics,
02249                                           PRInt32*           aFontID)
02250 {
02251 #if 0
02252   aBoundingMetrics.Clear();
02253   if (!mFontMetrics) return NS_ERROR_FAILURE;
02254 
02255   SetupFontAndColor();
02256 
02257   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
02258   GetBoundingMetricsData data = {mDC, mCurrFont, &aBoundingMetrics, PR_TRUE, NS_OK};
02259 
02260   nsresult rv = metrics->ResolveForwards(mDC, aString, aLength, do_GetBoundingMetrics, &data);
02261   if (NS_SUCCEEDED(rv)) {
02262     rv = data.mStatus;
02263   }
02264 
02265   // convert to app units
02266   aBoundingMetrics.leftBearing = NSToCoordRound(float(aBoundingMetrics.leftBearing) * mP2T);
02267   aBoundingMetrics.rightBearing = NSToCoordRound(float(aBoundingMetrics.rightBearing) * mP2T);
02268   aBoundingMetrics.width = NSToCoordRound(float(aBoundingMetrics.width) * mP2T);
02269   aBoundingMetrics.ascent = NSToCoordRound(float(aBoundingMetrics.ascent) * mP2T);
02270   aBoundingMetrics.descent = NSToCoordRound(float(aBoundingMetrics.descent) * mP2T);
02271 
02272   if (mCurrFont != data.mFont) {
02273     // If the font was changed along the way, restore our font
02274     ::SelectObject(mDC, mCurrFont);
02275   }
02276 
02277   if (aFontID) *aFontID = 0;
02278 
02279   return rv;
02280 #endif
02281   return NS_ERROR_FAILURE;
02282 }
02283 #endif // MOZ_MATHML
02284 
02285 NS_IMETHODIMP nsRenderingContextOS2::CopyOffScreenBits(
02286                      nsIDrawingSurface* aSrcSurf, PRInt32 aSrcX, PRInt32 aSrcY,
02287                      const nsRect &aDestBounds, PRUint32 aCopyFlags)
02288 {
02289    NS_ASSERTION( aSrcSurf && mSurface && mMainSurface, "bad surfaces");
02290 
02291    nsDrawingSurfaceOS2 *SourceSurf = (nsDrawingSurfaceOS2 *) aSrcSurf;
02292    nsDrawingSurfaceOS2 *DestSurf;
02293    HPS DestPS;
02294 
02295    if (aCopyFlags & NS_COPYBITS_TO_BACK_BUFFER)
02296    {
02297       DestSurf = mSurface;
02298       DestPS   = mPS;
02299    } else
02300    {
02301       DestSurf = mMainSurface;
02302       DestPS   = mMainSurface->GetPS();
02303    }
02304 
02305    PRUint32 DestHeight   = DestSurf->GetHeight();
02306    PRUint32 SourceHeight = SourceSurf->GetHeight();
02307 
02308 
02309    if (aCopyFlags & NS_COPYBITS_USE_SOURCE_CLIP_REGION)
02310    {
02311       HRGN SourceClipRegion = OS2_CopyClipRegion(SourceSurf->GetPS());
02312 
02313       // Currently clip region is in offscreen drawing surface coordinate system.
02314       // If offscreen surface have different height than destination surface,
02315       // then clipping region should be shifted down by amount of this difference.
02316 
02317       if ( (SourceClipRegion) && (SourceHeight != DestHeight) )
02318       {
02319          POINTL Offset = { 0, -(SourceHeight - DestHeight) };
02320 
02321          GFX (::GpiOffsetRegion(DestPS, SourceClipRegion, &Offset), FALSE);
02322       }
02323 
02324       OS2_SetClipRegion(DestPS, SourceClipRegion);
02325    }
02326 
02327 
02328    nsRect drect(aDestBounds);
02329 
02330    if( aCopyFlags & NS_COPYBITS_XFORM_SOURCE_VALUES)
02331       mTranMatrix->TransformCoord( &aSrcX, &aSrcY);
02332 
02333    if( aCopyFlags & NS_COPYBITS_XFORM_DEST_VALUES)
02334       mTranMatrix->TransformCoord( &drect.x, &drect.y,
02335                                &drect.width, &drect.height);
02336 
02337    // Note rects for GpiBitBlt are in-ex.
02338 
02339    POINTL Points [3] = { {drect.x, DestHeight - drect.y - drect.height},    // TLL
02340                          {drect.x + drect.width, DestHeight - drect.y},     // TUR
02341                          {aSrcX, SourceHeight - aSrcY - drect.height} };    // SLL
02342 
02343 
02344    GFX (::GpiBitBlt(DestPS, SourceSurf->GetPS(), 3, Points, ROP_SRCCOPY, BBO_OR), GPI_ERROR);
02345 
02346    return NS_OK;
02347 }
02348 
02349 NS_IMETHODIMP nsRenderingContextOS2::RetrieveCurrentNativeGraphicData(void** ngd)
02350 {
02351   if(ngd != nsnull)
02352     *ngd = (void*)mPS;
02353   return NS_OK;
02354 }
02355 
02356 void nsRenderingContextOS2::SetupFontAndColor(void)
02357 {
02358   if (mFontMetrics) {
02359     // select font
02360     nsFontHandle fontHandle;
02361     mFontMetrics->GetFontHandle(fontHandle);
02362     if (!mCurrFontOS2 || mCurrFontOS2 != fontHandle) {
02363       mCurrFontOS2 = (nsFontOS2*)fontHandle;
02364       mSurface->SelectFont(mCurrFontOS2);
02365     }
02366   }
02367 
02368   // XXX - should we be always setting the color?
02369   CHARBUNDLE cBundle;
02370   cBundle.lColor = GetGPIColor();
02371   cBundle.usMixMode = FM_OVERPAINT;
02372   cBundle.usBackMixMode = BM_LEAVEALONE;
02373 
02374   GFX (::GpiSetAttrs(mPS, PRIM_CHAR,
02375                      CBB_COLOR | CBB_MIX_MODE | CBB_BACK_MIX_MODE,
02376                      0, &cBundle),
02377        FALSE);
02378 }
02379 
02380 void nsRenderingContextOS2::PushClipState(void)
02381 {                           
02382   if (!(mStates->mFlags & FLAG_CLIP_CHANGED))
02383   {
02384     GraphicsState *tstate = mStates->mNext;
02385 
02386     //we have never set a clip on this state before, so
02387     //remember the current clip state in the next state on the
02388     //stack. kind of wacky, but avoids selecting stuff in the DC
02389     //all the damned time.
02390 
02391     if (nsnull != tstate)
02392     {
02393       // Attempt to fix region leak
02394       if( tstate->mClipRegion )
02395       {
02396          GpiDestroyRegion( mPS, tstate->mClipRegion );
02397       }
02398 
02399       tstate->mClipRegion = OS2_CopyClipRegion( mPS );
02400 
02401       if (tstate->mClipRegion != 0)
02402         tstate->mFlags |= FLAG_CLIP_VALID;
02403       else
02404         tstate->mFlags &= ~FLAG_CLIP_VALID;
02405     }
02406   
02407     mStates->mFlags |= FLAG_CLIP_CHANGED;
02408   }
02409 }
02410 
02411 // Clip region helper functions --------------------------------------------
02412 
02413 /* hrgnCombine becomes the clip region.  Any current clip region is       */
02414 /* dealt with.  hrgnCombine may be NULLHANDLE.                            */
02415 /* Return value is lComplexity.                                           */
02416 /* lMode should be CRGN_*                                                 */
02417 LONG OS2_CombineClipRegion( HPS hps, HRGN hrgnCombine, LONG lMode)
02418 {
02419    if (!hps) return RGN_ERROR;
02420 
02421    HRGN hrgnClip = 0;
02422    LONG rc = RGN_NULL;
02423 
02424    GFX (::GpiSetClipRegion (hps, 0, &hrgnClip), RGN_ERROR); // Get the current clip region and deselect it
02425 
02426    if (hrgnClip && hrgnClip != HRGN_ERROR)
02427    {
02428       if (lMode != CRGN_COPY)    // If necessarry combine with previous clip region
02429          GFX (::GpiCombineRegion (hps, hrgnCombine, hrgnClip, hrgnCombine, lMode), RGN_ERROR);
02430       
02431       GFX (::GpiDestroyRegion (hps, hrgnClip), FALSE);
02432    }
02433 
02434    if (hrgnCombine)
02435       rc = GFX (::GpiSetClipRegion (hps, hrgnCombine, NULL), RGN_ERROR);  // Set new clip region
02436 
02437    return rc;
02438 }
02439 
02440 /* Return value is HRGN_ */
02441 HRGN OS2_CopyClipRegion( HPS hps)
02442 {
02443   if (!hps) return HRGN_ERROR;
02444 
02445   HRGN hrgn = 0, hrgnClip;
02446 
02447   // Get current clip region
02448   GFX (::GpiSetClipRegion (hps, 0, &hrgnClip), RGN_ERROR);
02449 
02450   if (hrgnClip && hrgnClip != HRGN_ERROR)
02451   {
02452      hrgn = GFX (::GpiCreateRegion (hps, 0, NULL), RGN_ERROR);     // Create empty region and combine with current
02453      GFX (::GpiCombineRegion (hps, hrgn, hrgnClip, 0, CRGN_COPY), RGN_ERROR);
02454      GFX (::GpiSetClipRegion (hps, hrgnClip, NULL), RGN_ERROR);    // restore current clip region
02455   }
02456 
02457   return hrgn;
02458 }
02459 
02460 // Keep coordinate within 32-bit limits
02461 NS_IMETHODIMP nsRenderingContextOS2::ConditionRect(nscoord &x, nscoord &y, nscoord &w, nscoord &h)
02462 {
02463   if (y < -134217728)
02464     y = -134217728;
02465 
02466   if (y + h > 134217727)
02467     h  = 134217727 - y;
02468 
02469   if (x < -134217728)
02470     x = -134217728;
02471 
02472   if (x + w > 134217727)
02473     w  = 134217727 - x;
02474 
02475   return NS_OK;
02476 }