Back to index

lightning-sunbird  0.9+nobinonly
nsRenderingContextWin.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  *   Roger B. Sidje <rbs@maths.uq.edu.au>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsRenderingContextWin.h"
00040 #include "nsICharRepresentable.h"
00041 #include "nsFontMetricsWin.h"
00042 #include "nsRegionWin.h"
00043 #include <math.h>
00044 #include "nsDeviceContextWin.h"
00045 #include "prprf.h"
00046 #include "nsDrawingSurfaceWin.h"
00047 #include "nsGfxCIID.h"
00048 #include "nsUnicharUtils.h"
00049 
00050 //#define GFX_DEBUG
00051 
00052 #ifdef GFX_DEBUG
00053   #define BREAK_TO_DEBUGGER           DebugBreak()
00054 #else   
00055   #define BREAK_TO_DEBUGGER
00056 #endif  
00057 
00058 #ifdef GFX_DEBUG
00059   #define VERIFY(exp)                 ((exp) ? 0: (GetLastError(), BREAK_TO_DEBUGGER))
00060 #else   // !_DEBUG
00061   #define VERIFY(exp)                 (exp)
00062 #endif  // !_DEBUG
00063 
00064 
00065 static NS_DEFINE_IID(kIRenderingContextIID, NS_IRENDERING_CONTEXT_IID);
00066 static NS_DEFINE_IID(kIRenderingContextWinIID, NS_IRENDERING_CONTEXT_WIN_IID);
00067 
00068 #define FLAG_CLIP_VALID       0x0001
00069 #define FLAG_CLIP_CHANGED     0x0002
00070 #define FLAG_LOCAL_CLIP_VALID 0x0004
00071 
00072 #define FLAGS_ALL             (FLAG_CLIP_VALID | FLAG_CLIP_CHANGED | FLAG_LOCAL_CLIP_VALID)
00073 
00074 // Macro for creating a palette relative color if you have a COLORREF instead
00075 // of the reg, green, and blue values. The color is color-matches to the nearest
00076 // in the current logical palette. This has no effect on a non-palette device
00077 #define PALETTERGB_COLORREF(c)  (0x02000000 | (c))
00078 
00079 typedef struct lineddastructtag
00080 {
00081    int   nDottedPixel;
00082    HDC   dc;
00083    COLORREF crColor;
00084 } lineddastruct;
00085 
00086 void CALLBACK LineDDAFunc(int x,int y,LONG lData)
00087 {
00088   lineddastruct * dda_struct = (lineddastruct *) lData;
00089   
00090   dda_struct->nDottedPixel ^= 1; 
00091 
00092   if (dda_struct->nDottedPixel)
00093     SetPixel(dda_struct->dc, x, y, dda_struct->crColor);
00094 }   
00095 
00096 
00097 
00098 class GraphicsState
00099 {
00100 public:
00101   GraphicsState();
00102   GraphicsState(GraphicsState &aState);
00103   ~GraphicsState();
00104 
00105   GraphicsState   *mNext;
00106   nsTransform2D   mMatrix;
00107   nsRect          mLocalClip;
00108   HRGN            mClipRegion;
00109   nscolor         mColor;
00110   COLORREF        mColorREF;
00111   nsIFontMetrics  *mFontMetrics;
00112   HPEN            mSolidPen;
00113   HPEN            mDashedPen;
00114   HPEN            mDottedPen;
00115   PRInt32         mFlags;
00116   nsLineStyle     mLineStyle;
00117 };
00118 
00119 GraphicsState :: GraphicsState()
00120 {
00121   mNext = nsnull;
00122   mMatrix.SetToIdentity();
00123   mLocalClip.x = mLocalClip.y = mLocalClip.width = mLocalClip.height = 0;
00124   mClipRegion = NULL;
00125   mColor = NS_RGB(0, 0, 0);
00126   mColorREF = RGB(0, 0, 0);
00127   mFontMetrics = nsnull;
00128   mSolidPen = NULL;
00129   mDashedPen = NULL;
00130   mDottedPen = NULL;
00131   mFlags = ~FLAGS_ALL;
00132   mLineStyle = nsLineStyle_kSolid;
00133 }
00134 
00135 GraphicsState :: GraphicsState(GraphicsState &aState) :
00136                                mMatrix(&aState.mMatrix),
00137                                mLocalClip(aState.mLocalClip)
00138 {
00139   mNext = &aState;
00140   mClipRegion = NULL;
00141   mColor = NS_RGB(0, 0, 0);
00142   mColorREF = RGB(0, 0, 0);
00143   mFontMetrics = nsnull;
00144   mSolidPen = NULL;
00145   mDashedPen = NULL;
00146   mDottedPen = NULL;
00147   mFlags = ~FLAGS_ALL;
00148   mLineStyle = aState.mLineStyle;
00149 }
00150 
00151 GraphicsState :: ~GraphicsState()
00152 {
00153   if (NULL != mClipRegion)
00154   {
00155     VERIFY(::DeleteObject(mClipRegion));
00156     mClipRegion = NULL;
00157   }
00158 
00159   //these are killed by the rendering context...
00160   mSolidPen = NULL;
00161   mDashedPen = NULL;
00162   mDottedPen = NULL;
00163 }
00164 
00165 // gIsWIN95 includes Win98 and WinME too
00166 #define NOT_SETUP 0x33
00167 static PRBool gIsWIN95 = NOT_SETUP;
00168 
00169 #define DONT_INIT 0
00170 static DWORD gBidiInfo = NOT_SETUP;
00171 
00172 // A few of the stock objects are needed all the time, so we just get them
00173 // once
00174 static HFONT  gStockSystemFont = (HFONT)::GetStockObject(SYSTEM_FONT);
00175 static HBRUSH gStockWhiteBrush = (HBRUSH)::GetStockObject(WHITE_BRUSH);
00176 static HPEN   gStockBlackPen = (HPEN)::GetStockObject(BLACK_PEN);
00177 static HPEN   gStockWhitePen = (HPEN)::GetStockObject(WHITE_PEN);
00178 
00179 nsRenderingContextWin :: nsRenderingContextWin()
00180 {
00181 #ifdef WINCE
00182   gIsWIN95 = PR_FALSE;
00183   gBidiInfo = DONT_INIT;
00184 #else
00185   // The first time in we initialize gIsWIN95 flag
00186   if (NOT_SETUP == gIsWIN95) {
00187     OSVERSIONINFO os;
00188     os.dwOSVersionInfoSize = sizeof(os);
00189     ::GetVersionEx(&os);
00190     // gIsWIN95 must be true for Win98 and WinME too
00191     if (VER_PLATFORM_WIN32_NT == os.dwPlatformId) {
00192       gIsWIN95 = PR_FALSE;
00193     }
00194     else {
00195       gIsWIN95 = PR_TRUE;
00196 
00197       if ( (os.dwMajorVersion < 4)
00198            || ( (os.dwMajorVersion == 4) && (os.dwMinorVersion == 0) ) ) {
00199         // Windows 95 or earlier: assume it's not Bidi
00200         gBidiInfo = DONT_INIT;
00201       }
00202       else if (os.dwMajorVersion >= 4) {
00203         // Windows 98 or later
00204         UINT cp = ::GetACP();
00205         if (1256 == cp) {
00206           gBidiInfo = GCP_REORDER | GCP_GLYPHSHAPE;
00207         }
00208         else if (1255 == cp) {
00209           gBidiInfo = GCP_REORDER;
00210         }
00211       }
00212     }
00213   }
00214 #endif
00215 
00216   mDC = NULL;
00217   mMainDC = NULL;
00218   mDCOwner = nsnull;
00219   mFontMetrics = nsnull;
00220   mOrigSolidBrush = NULL;
00221   mOrigFont = NULL;
00222   mOrigSolidPen = NULL;
00223   mCurrBrushColor = RGB(255, 255, 255);
00224   mCurrFontWin = nsnull;
00225   mCurrPenColor = NULL;
00226   mCurrPen = NULL;
00227   mNullPen = NULL;
00228   mCurrTextColor = RGB(0, 0, 0);
00229   mCurrLineStyle = nsLineStyle_kSolid;
00230 #ifdef NS_DEBUG
00231   mInitialized = PR_FALSE;
00232 #endif
00233   mSurface = nsnull;
00234   mMainSurface = nsnull;
00235 
00236   mStateCache = new nsVoidArray();
00237   mRightToLeftText = PR_FALSE;
00238 
00239   //create an initial GraphicsState
00240 
00241   PushState();
00242 
00243   mP2T = 1.0f;
00244 }
00245 
00246 nsRenderingContextWin :: ~nsRenderingContextWin()
00247 {
00248   NS_IF_RELEASE(mContext);
00249   NS_IF_RELEASE(mFontMetrics);
00250 
00251   //destroy the initial GraphicsState
00252 
00253   PopState();
00254 
00255   //cleanup the DC so that we can just destroy objects
00256   //in the graphics state without worrying that we are
00257   //ruining the dc
00258 
00259   if (NULL != mDC)
00260   {
00261     if (NULL != mOrigSolidBrush)
00262     {
00263       ::SelectObject(mDC, mOrigSolidBrush);
00264       mOrigSolidBrush = NULL;
00265     }
00266 
00267     if (NULL != mOrigFont)
00268     {
00269       ::SelectObject(mDC, mOrigFont);
00270       mOrigFont = NULL;
00271     }
00272 
00273     if (NULL != mOrigSolidPen)
00274     {
00275       ::SelectObject(mDC, mOrigSolidPen);
00276       mOrigSolidPen = NULL;
00277     }
00278   }
00279 
00280   mCurrBrush = NULL;   // don't delete - owned by brush cache
00281 
00282   //don't kill the font because the font cache/metrics owns it
00283   mCurrFont = NULL;
00284 
00285   if (mCurrPen && (mCurrPen != gStockBlackPen) && (mCurrPen != gStockWhitePen)) {
00286     VERIFY(::DeleteObject(mCurrPen));
00287   }
00288 
00289   if ((NULL != mNullPen) && (mNullPen != mCurrPen)) {
00290     VERIFY(::DeleteObject(mNullPen));
00291   }
00292 
00293   mCurrPen = NULL;
00294   mNullPen = NULL;
00295 
00296   if (nsnull != mStateCache)
00297   {
00298     PRInt32 cnt = mStateCache->Count();
00299 
00300     while (--cnt >= 0)
00301     {
00302       GraphicsState *state = (GraphicsState *)mStateCache->ElementAt(cnt);
00303       mStateCache->RemoveElementAt(cnt);
00304 
00305       if (nsnull != state)
00306         delete state;
00307     }
00308 
00309     delete mStateCache;
00310     mStateCache = nsnull;
00311   }
00312 
00313   if (nsnull != mSurface)
00314   {
00315     mSurface->ReleaseDC();
00316     NS_RELEASE(mSurface);
00317   }
00318 
00319   if (nsnull != mMainSurface)
00320   {
00321     mMainSurface->ReleaseDC();
00322     NS_RELEASE(mMainSurface);
00323   }
00324 
00325   if (nsnull != mDCOwner)
00326   {
00327     ::ReleaseDC((HWND)mDCOwner->GetNativeData(NS_NATIVE_WINDOW), mMainDC);
00328     NS_RELEASE(mDCOwner);
00329   }
00330 
00331   mTranMatrix = nsnull;
00332   mDC = NULL;
00333   mMainDC = NULL;
00334 }
00335 
00336 nsresult
00337 nsRenderingContextWin :: QueryInterface(REFNSIID aIID, void** aInstancePtr)
00338 {
00339   if (nsnull == aInstancePtr)
00340     return NS_ERROR_NULL_POINTER;
00341 
00342   if (aIID.Equals(kIRenderingContextIID))
00343   {
00344     nsIRenderingContext* tmp = this;
00345     *aInstancePtr = (void*) tmp;
00346     NS_ADDREF_THIS();
00347     return NS_OK;
00348   }
00349 
00350   if (aIID.Equals(kIRenderingContextWinIID))
00351   {
00352     nsIRenderingContextWin* tmp = this;
00353     *aInstancePtr = (void*) tmp;
00354     NS_ADDREF_THIS();
00355     return NS_OK;
00356   }
00357 
00358   static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
00359 
00360   if (aIID.Equals(kISupportsIID))
00361   {
00362     nsIRenderingContext* tmp = this;
00363     nsISupports* tmp2 = tmp;
00364     *aInstancePtr = (void*) tmp2;
00365     NS_ADDREF_THIS();
00366     return NS_OK;
00367   }
00368 
00369   return NS_NOINTERFACE;
00370 }
00371 
00372 NS_IMPL_ADDREF(nsRenderingContextWin)
00373 NS_IMPL_RELEASE(nsRenderingContextWin)
00374 
00375 NS_IMETHODIMP
00376 nsRenderingContextWin :: Init(nsIDeviceContext* aContext,
00377                               nsIWidget *aWindow)
00378 {
00379   NS_PRECONDITION(PR_FALSE == mInitialized, "double init");
00380 
00381   mContext = aContext;
00382   NS_IF_ADDREF(mContext);
00383 
00384   mSurface = (nsDrawingSurfaceWin *)new nsDrawingSurfaceWin();
00385 
00386   if (nsnull != mSurface)
00387   {
00388     HDC tdc = (HDC)aWindow->GetNativeData(NS_NATIVE_GRAPHIC);
00389 
00390     NS_ADDREF(mSurface);
00391     mSurface->Init(tdc);
00392     mDC = tdc;
00393 
00394     mMainDC = mDC;
00395     mMainSurface = mSurface;
00396     NS_ADDREF(mMainSurface);
00397   }
00398 
00399   mDCOwner = aWindow;
00400 
00401   NS_IF_ADDREF(mDCOwner);
00402 
00403   return CommonInit();
00404 }
00405 
00406 NS_IMETHODIMP
00407 nsRenderingContextWin :: Init(nsIDeviceContext* aContext,
00408                               nsIDrawingSurface* aSurface)
00409 {
00410   NS_PRECONDITION(PR_FALSE == mInitialized, "double init");
00411 
00412   mContext = aContext;
00413   NS_IF_ADDREF(mContext);
00414 
00415   mSurface = (nsDrawingSurfaceWin *)aSurface;
00416 
00417   if (nsnull != mSurface)
00418   {
00419     NS_ADDREF(mSurface);
00420     mSurface->GetDC(&mDC);
00421 
00422     mMainDC = mDC;
00423     mMainSurface = mSurface;
00424     NS_ADDREF(mMainSurface);
00425   }
00426 
00427   mDCOwner = nsnull;
00428 
00429   return CommonInit();
00430 }
00431 
00432 nsresult nsRenderingContextWin :: SetupDC(HDC aOldDC, HDC aNewDC)
00433 {
00434   HBRUSH  prevbrush;
00435   HFONT   prevfont;
00436   HPEN    prevpen;
00437 
00438   ::SetTextColor(aNewDC, PALETTERGB_COLORREF(mColor));
00439   mCurrTextColor = mCurrentColor;
00440   ::SetBkMode(aNewDC, TRANSPARENT);
00441 #ifndef WINCE
00442   ::SetPolyFillMode(aNewDC, WINDING);
00443 #endif
00444   // Temporary fix for bug 135226 until we do better decode-time
00445   // dithering and paletized storage of images.
00446   nsPaletteInfo palInfo;
00447   mContext->GetPaletteInfo(palInfo);
00448 #ifndef WINCE
00449   if (palInfo.isPaletteDevice)
00450     ::SetStretchBltMode(aNewDC, HALFTONE);
00451   else
00452     ::SetStretchBltMode(aNewDC, COLORONCOLOR);                                  
00453 #endif
00454 
00455   ::SetTextAlign(aNewDC, TA_BASELINE);
00456 
00457   if (nsnull != aOldDC)
00458   {
00459     if (nsnull != mOrigSolidBrush)
00460       prevbrush = (HBRUSH)::SelectObject(aOldDC, mOrigSolidBrush);
00461 
00462     if (nsnull != mOrigFont)
00463       prevfont = (HFONT)::SelectObject(aOldDC, mOrigFont);
00464 
00465     if (nsnull != mOrigSolidPen)
00466       prevpen = (HPEN)::SelectObject(aOldDC, mOrigSolidPen);
00467 
00468   }
00469   else
00470   {
00471     prevbrush = gStockWhiteBrush;
00472     prevfont = gStockSystemFont;
00473     prevpen = gStockBlackPen;
00474   }
00475 
00476   mOrigSolidBrush = (HBRUSH)::SelectObject(aNewDC, prevbrush);
00477   mOrigFont = (HFONT)::SelectObject(aNewDC, prevfont);
00478   mOrigSolidPen = (HPEN)::SelectObject(aNewDC, prevpen);
00479 
00480 #if 0
00481   GraphicsState *pstate = mStates;
00482 
00483   while ((nsnull != pstate) && !(pstate->mFlags & FLAG_CLIP_VALID))
00484     pstate = pstate->mNext;
00485 
00486   if (nsnull != pstate)
00487     ::SelectClipRgn(aNewDC, pstate->mClipRegion);
00488 #endif
00489   
00490   // If this is a palette device, then select and realize the palette
00491   if (palInfo.isPaletteDevice && palInfo.palette)
00492   {
00493     // Select the palette in the background
00494     ::SelectPalette(aNewDC, (HPALETTE)palInfo.palette, PR_TRUE);
00495     ::RealizePalette(aNewDC);
00496   }
00497 
00498   return NS_OK;
00499 }
00500 
00501 nsresult nsRenderingContextWin :: CommonInit(void)
00502 {
00503   float app2dev;
00504 
00505   app2dev = mContext->AppUnitsToDevUnits();
00506        mTranMatrix->AddScale(app2dev, app2dev);
00507   mP2T = mContext->DevUnitsToAppUnits();
00508 
00509 #ifdef NS_DEBUG
00510   mInitialized = PR_TRUE;
00511 #endif
00512 
00513   return SetupDC(nsnull, mDC);
00514 }
00515 
00516 NS_IMETHODIMP nsRenderingContextWin :: LockDrawingSurface(PRInt32 aX, PRInt32 aY,
00517                                                           PRUint32 aWidth, PRUint32 aHeight,
00518                                                           void **aBits, PRInt32 *aStride,
00519                                                           PRInt32 *aWidthBytes, PRUint32 aFlags)
00520 {
00521   PRBool        destructive;
00522   nsPaletteInfo palInfo;
00523 
00524   PushState();
00525 
00526   mSurface->IsReleaseDCDestructive(&destructive);
00527 
00528   if (destructive)
00529   {
00530     PushClipState();
00531 
00532     if (nsnull != mOrigSolidBrush)
00533       mCurrBrush = (HBRUSH)::SelectObject(mDC, mOrigSolidBrush);
00534 
00535     if (nsnull != mOrigFont)
00536       mCurrFont = (HFONT)::SelectObject(mDC, mOrigFont);
00537 
00538     if (nsnull != mOrigSolidPen)
00539       mCurrPen = (HPEN)::SelectObject(mDC, mOrigSolidPen);
00540 
00541     mContext->GetPaletteInfo(palInfo);
00542     if(palInfo.isPaletteDevice && palInfo.palette){
00543       ::SelectPalette(mDC,(HPALETTE)palInfo.palette,PR_TRUE);
00544       ::RealizePalette(mDC);
00545 #ifndef WINCE
00546       ::UpdateColors(mDC);                                                      
00547 #endif
00548     }
00549   }
00550 
00551   mSurface->ReleaseDC();
00552 
00553   return mSurface->Lock(aX, aY, aWidth, aHeight, aBits, aStride, aWidthBytes, aFlags);
00554 }
00555 
00556 NS_IMETHODIMP nsRenderingContextWin :: UnlockDrawingSurface(void)
00557 {
00558   PRBool  clipstate;
00559 
00560   mSurface->Unlock();
00561   mSurface->GetDC(&mDC);
00562 
00563   PopState();
00564 
00565   mSurface->IsReleaseDCDestructive(&clipstate);
00566 
00567   if (clipstate)
00568   {
00569     ::SetTextColor(mDC, PALETTERGB_COLORREF(mColor));
00570     mCurrTextColor = mCurrentColor;
00571 
00572     ::SetBkMode(mDC, TRANSPARENT);
00573 #ifndef WINCE
00574     ::SetPolyFillMode(mDC, WINDING);
00575     ::SetStretchBltMode(mDC, COLORONCOLOR);
00576 #endif
00577 
00578     mOrigSolidBrush = (HBRUSH)::SelectObject(mDC, mCurrBrush);
00579     mOrigFont = (HFONT)::SelectObject(mDC, mCurrFont);
00580     mOrigSolidPen = (HPEN)::SelectObject(mDC, mCurrPen);
00581   }
00582 
00583   return NS_OK;
00584 }
00585 
00586 NS_IMETHODIMP
00587 nsRenderingContextWin :: SelectOffScreenDrawingSurface(nsIDrawingSurface* aSurface)
00588 {
00589   nsresult  rv;
00590 
00591   //XXX this should reset the data in the state stack.
00592 
00593   if (aSurface != mSurface)
00594   {
00595     if (nsnull != aSurface)
00596     {
00597       HDC tdc;
00598 
00599       //get back a DC
00600       ((nsDrawingSurfaceWin *)aSurface)->GetDC(&tdc);
00601 
00602       rv = SetupDC(mDC, tdc);
00603 
00604       //kill the DC
00605       mSurface->ReleaseDC();
00606 
00607       NS_IF_RELEASE(mSurface);
00608       mSurface = (nsDrawingSurfaceWin *)aSurface;
00609     }
00610     else
00611     {
00612       if (NULL != mDC)
00613       {
00614         rv = SetupDC(mDC, mMainDC);
00615 
00616         //kill the DC
00617         mSurface->ReleaseDC();
00618 
00619         NS_IF_RELEASE(mSurface);
00620         mSurface = mMainSurface;
00621       }
00622     }
00623 
00624     NS_ADDREF(mSurface);
00625     mSurface->GetDC(&mDC);
00626   }
00627   else
00628     rv = NS_OK;
00629 
00630   return rv;
00631 }
00632 
00633 NS_IMETHODIMP
00634 nsRenderingContextWin :: GetDrawingSurface(nsIDrawingSurface* *aSurface)
00635 {
00636   *aSurface = mSurface;
00637   return NS_OK;
00638 }
00639 
00640 NS_IMETHODIMP
00641 nsRenderingContextWin :: GetHints(PRUint32& aResult)
00642 {
00643   PRUint32 result = 0;
00644   
00645 #ifndef WINCE
00646   result |= NS_RENDERING_HINT_FAST_MEASURE;
00647 #endif
00648 
00649   if (gIsWIN95)
00650     result |= NS_RENDERING_HINT_FAST_8BIT_TEXT;
00651 
00652   if (NOT_SETUP == gBidiInfo) {
00653     InitBidiInfo();
00654   }
00655 #ifndef WINCE
00656   if (GCP_REORDER == (gBidiInfo & GCP_REORDER) )
00657     result |= NS_RENDERING_HINT_BIDI_REORDERING;
00658   if (GCP_GLYPHSHAPE == (gBidiInfo & GCP_GLYPHSHAPE) )
00659     result |= NS_RENDERING_HINT_ARABIC_SHAPING;
00660 #endif
00661   
00662   aResult = result;
00663 
00664   return NS_OK;
00665 }
00666 
00667 NS_IMETHODIMP nsRenderingContextWin :: Reset()
00668 {
00669   return NS_OK;
00670 }
00671 
00672 NS_IMETHODIMP nsRenderingContextWin :: GetDeviceContext(nsIDeviceContext *&aContext)
00673 {
00674   NS_IF_ADDREF(mContext);
00675   aContext = mContext;
00676   return NS_OK;
00677 }
00678 
00679 NS_IMETHODIMP nsRenderingContextWin :: PushState(void)
00680 {
00681   PRInt32 cnt = mStateCache->Count();
00682 
00683   if (cnt == 0)
00684   {
00685     if (nsnull == mStates)
00686       mStates = new GraphicsState();
00687     else
00688       mStates = new GraphicsState(*mStates);
00689   }
00690   else
00691   {
00692     GraphicsState *state = (GraphicsState *)mStateCache->ElementAt(cnt - 1);
00693     mStateCache->RemoveElementAt(cnt - 1);
00694 
00695     state->mNext = mStates;
00696 
00697     //clone state info
00698 
00699     state->mMatrix = mStates->mMatrix;
00700     state->mLocalClip = mStates->mLocalClip;
00701 // we don't want to NULL this out since we reuse the region
00702 // from state to state. if we NULL it, we need to also delete it,
00703 // which means we'll just re-create it when we push the clip state. MMP
00704 //    state->mClipRegion = NULL;
00705     state->mSolidPen = NULL;
00706     state->mDashedPen = NULL;
00707     state->mDottedPen = NULL;
00708     state->mFlags = ~FLAGS_ALL;
00709     state->mLineStyle = mStates->mLineStyle;
00710 
00711     mStates = state;
00712   }
00713 
00714   if (nsnull != mStates->mNext)
00715   {
00716     mStates->mNext->mColor = mCurrentColor;
00717     mStates->mNext->mColorREF = mColor;
00718     mStates->mNext->mFontMetrics = mFontMetrics;
00719     NS_IF_ADDREF(mStates->mNext->mFontMetrics);
00720   }
00721 
00722   mTranMatrix = &mStates->mMatrix;
00723 
00724   return NS_OK;
00725 }
00726 
00727 NS_IMETHODIMP nsRenderingContextWin :: PopState(void)
00728 {
00729   if (nsnull == mStates)
00730   {
00731     NS_ASSERTION(!(nsnull == mStates), "state underflow");
00732   }
00733   else
00734   {
00735     GraphicsState *oldstate = mStates;
00736 
00737     mStates = mStates->mNext;
00738 
00739     mStateCache->AppendElement(oldstate);
00740 
00741     if (nsnull != mStates)
00742     {
00743       mTranMatrix = &mStates->mMatrix;
00744 
00745       GraphicsState *pstate;
00746 
00747       if (oldstate->mFlags & FLAG_CLIP_CHANGED)
00748       {
00749         pstate = mStates;
00750 
00751         //the clip rect has changed from state to state, so
00752         //install the previous clip rect
00753 
00754         while ((nsnull != pstate) && !(pstate->mFlags & FLAG_CLIP_VALID))
00755           pstate = pstate->mNext;
00756 
00757         if (nsnull != pstate)
00758           ::SelectClipRgn(mDC, pstate->mClipRegion);
00759       }
00760 
00761       oldstate->mFlags &= ~FLAGS_ALL;
00762       oldstate->mSolidPen = NULL;
00763       oldstate->mDashedPen = NULL;
00764       oldstate->mDottedPen = NULL;
00765 
00766       NS_IF_RELEASE(mFontMetrics);
00767       mFontMetrics = mStates->mFontMetrics;
00768 
00769       mCurrentColor = mStates->mColor;
00770       mColor = mStates->mColorREF;
00771 
00772       SetLineStyle(mStates->mLineStyle);
00773     }
00774     else
00775       mTranMatrix = nsnull;
00776   }
00777 
00778   return NS_OK;
00779 }
00780 
00781 NS_IMETHODIMP nsRenderingContextWin :: IsVisibleRect(const nsRect& aRect, PRBool &aVisible)
00782 {
00783   aVisible = PR_TRUE;
00784   return NS_OK;
00785 }
00786 
00787 NS_IMETHODIMP nsRenderingContextWin :: SetClipRect(const nsRect& aRect, nsClipCombine aCombine)
00788 {
00789   nsRect  trect = aRect;
00790 
00791   mStates->mLocalClip = aRect;
00792 
00793        mTranMatrix->TransformCoord(&trect.x, &trect.y,
00794                            &trect.width, &trect.height);
00795 
00796   RECT nr;
00797   ConditionRect(trect, nr);
00798 
00799   mStates->mFlags |= FLAG_LOCAL_CLIP_VALID;
00800 
00801   //how we combine the new rect with the previous?
00802 
00803   if (aCombine == nsClipCombine_kIntersect)
00804   {
00805     PushClipState();
00806 
00807     ::IntersectClipRect(mDC, nr.left,
00808                         nr.top,
00809                         nr.right,
00810                         nr.bottom);
00811   }
00812   else if (aCombine == nsClipCombine_kUnion)
00813   {
00814     PushClipState();
00815 
00816     HRGN  tregion = ::CreateRectRgn(nr.left,
00817                                     nr.top,
00818                                     nr.right,
00819                                     nr.bottom);
00820 
00821     ::ExtSelectClipRgn(mDC, tregion, RGN_OR);
00822     ::DeleteObject(tregion);
00823   }
00824   else if (aCombine == nsClipCombine_kSubtract)
00825   {
00826     PushClipState();
00827 
00828     ::ExcludeClipRect(mDC, nr.left,
00829                       nr.top,
00830                       nr.right,
00831                       nr.bottom);
00832   }
00833   else if (aCombine == nsClipCombine_kReplace)
00834   {
00835     PushClipState();
00836 
00837     HRGN  tregion = ::CreateRectRgn(nr.left,
00838                                     nr.top,
00839                                     nr.right,
00840                                     nr.bottom);
00841     ::SelectClipRgn(mDC, tregion);
00842     ::DeleteObject(tregion);
00843   }
00844   else
00845     NS_ASSERTION(PR_FALSE, "illegal clip combination");
00846 
00847   return NS_OK;
00848 }
00849 
00850 NS_IMETHODIMP nsRenderingContextWin :: GetClipRect(nsRect &aRect, PRBool &aClipValid)
00851 {
00852   if (mStates->mFlags & FLAG_LOCAL_CLIP_VALID)
00853   {
00854     aRect = mStates->mLocalClip;
00855     aClipValid = PR_TRUE;
00856   }
00857   else
00858     aClipValid = PR_FALSE;
00859 
00860   return NS_OK;
00861 }
00862 
00863 NS_IMETHODIMP nsRenderingContextWin :: SetClipRegion(const nsIRegion& aRegion, nsClipCombine aCombine)
00864 {
00865   HRGN        hrgn;
00866   int         cmode;
00867 
00868   aRegion.GetNativeRegion((void *&)hrgn);
00869 
00870   switch (aCombine)
00871   {
00872     case nsClipCombine_kIntersect:
00873       cmode = RGN_AND;
00874       break;
00875 
00876     case nsClipCombine_kUnion:
00877       cmode = RGN_OR;
00878       break;
00879 
00880     case nsClipCombine_kSubtract:
00881       cmode = RGN_DIFF;
00882       break;
00883 
00884     default:
00885     case nsClipCombine_kReplace:
00886       cmode = RGN_COPY;
00887       break;
00888   }
00889 
00890   if (NULL != hrgn)
00891   {
00892     mStates->mFlags &= ~FLAG_LOCAL_CLIP_VALID;
00893     PushClipState();
00894     ::ExtSelectClipRgn(mDC, hrgn, cmode);
00895   }
00896   else
00897     return PR_FALSE;
00898 
00899   return NS_OK;
00900 }
00901 
00905 NS_IMETHODIMP nsRenderingContextWin::CopyClipRegion(nsIRegion &aRegion)
00906 {
00907   int rstat = ::GetClipRgn(mDC, ((nsRegionWin *)&aRegion)->mRegion);
00908 
00909   if (rstat == 1)
00910   {
00911     //i can't find a way to get the actual complexity
00912     //of the region without actually messing with it, so
00913     //if the region is non-empty, we'll call it complex. MMP
00914 
00915     ((nsRegionWin *)&aRegion)->mRegionType = eRegionComplexity_complex;
00916   }
00917  
00918   return NS_OK;
00919 }
00920 
00921 NS_IMETHODIMP nsRenderingContextWin :: GetClipRegion(nsIRegion **aRegion)
00922 {
00923   nsresult  rv = NS_OK;
00924 
00925   NS_ASSERTION(!(nsnull == aRegion), "no region ptr");
00926 
00927   if (nsnull == *aRegion)
00928   {
00929     nsRegionWin *rgn = new nsRegionWin();
00930 
00931     if (nsnull != rgn)
00932     {
00933       NS_ADDREF(rgn);
00934 
00935       rv = rgn->Init();
00936 
00937       if (NS_OK != rv)
00938         NS_RELEASE(rgn);
00939       else
00940         *aRegion = rgn;
00941     }
00942     else
00943       rv = NS_ERROR_OUT_OF_MEMORY;
00944   }
00945 
00946   if (rv == NS_OK) {
00947     rv = CopyClipRegion(**aRegion);
00948   }
00949 
00950   return rv;
00951 }
00952 
00953 NS_IMETHODIMP nsRenderingContextWin :: SetColor(nscolor aColor)
00954 {
00955   mCurrentColor = aColor;
00956   mColor = RGB(NS_GET_R(aColor),
00957                NS_GET_G(aColor),
00958                NS_GET_B(aColor));
00959   return NS_OK;
00960 }
00961 
00962 NS_IMETHODIMP nsRenderingContextWin :: GetColor(nscolor &aColor) const
00963 {
00964   aColor = mCurrentColor;
00965   return NS_OK;
00966 }
00967 
00968 NS_IMETHODIMP nsRenderingContextWin :: SetLineStyle(nsLineStyle aLineStyle)
00969 {
00970   mCurrLineStyle = aLineStyle;
00971   return NS_OK;
00972 }
00973 
00974 NS_IMETHODIMP nsRenderingContextWin :: GetLineStyle(nsLineStyle &aLineStyle)
00975 {
00976   aLineStyle = mCurrLineStyle;
00977   return NS_OK;
00978 }
00979 
00980 NS_IMETHODIMP nsRenderingContextWin :: SetFont(const nsFont& aFont, nsIAtom* aLangGroup)
00981 {
00982   mCurrFontWin = nsnull; // owned & released by mFontMetrics
00983   NS_IF_RELEASE(mFontMetrics);
00984   mContext->GetMetricsFor(aFont, aLangGroup, mFontMetrics);
00985 
00986   return NS_OK;
00987 }
00988 
00989 NS_IMETHODIMP nsRenderingContextWin :: SetFont(nsIFontMetrics *aFontMetrics)
00990 {
00991   mCurrFontWin = nsnull; // owned & released by mFontMetrics
00992   NS_IF_RELEASE(mFontMetrics);
00993   mFontMetrics = aFontMetrics;
00994   NS_IF_ADDREF(mFontMetrics);
00995 
00996   return NS_OK;
00997 }
00998 
00999 NS_IMETHODIMP nsRenderingContextWin :: GetFontMetrics(nsIFontMetrics *&aFontMetrics)
01000 {
01001   NS_IF_ADDREF(mFontMetrics);
01002   aFontMetrics = mFontMetrics;
01003 
01004   return NS_OK;
01005 }
01006 
01007 // add the passed in translation to the current translation
01008 NS_IMETHODIMP nsRenderingContextWin :: Translate(nscoord aX, nscoord aY)
01009 {
01010        mTranMatrix->AddTranslation((float)aX,(float)aY);
01011   return NS_OK;
01012 }
01013 
01014 // add the passed in scale to the current scale
01015 NS_IMETHODIMP nsRenderingContextWin :: Scale(float aSx, float aSy)
01016 {
01017        mTranMatrix->AddScale(aSx, aSy);
01018   return NS_OK;
01019 }
01020 
01021 NS_IMETHODIMP nsRenderingContextWin :: GetCurrentTransform(nsTransform2D *&aTransform)
01022 {
01023   aTransform = mTranMatrix;
01024   return NS_OK;
01025 }
01026 
01027 NS_IMETHODIMP nsRenderingContextWin :: CreateDrawingSurface(const nsRect& aBounds, PRUint32 aSurfFlags, nsIDrawingSurface* &aSurface)
01028 {
01029   nsDrawingSurfaceWin *surf = new nsDrawingSurfaceWin();
01030 
01031   if (nsnull != surf)
01032   {
01033     NS_ADDREF(surf);
01034 
01035     surf->Init(mMainDC, aBounds.width, aBounds.height, aSurfFlags);
01036   }
01037 
01038   aSurface = surf;
01039 
01040   return NS_OK;
01041 }
01042 
01043 NS_IMETHODIMP nsRenderingContextWin :: DestroyDrawingSurface(nsIDrawingSurface* aDS)
01044 {
01045   nsDrawingSurfaceWin *surf = (nsDrawingSurfaceWin *)aDS;
01046 
01047   //are we using the surface that we want to kill?
01048   if (surf == mSurface)
01049   {
01050     //remove our local ref to the surface
01051     NS_IF_RELEASE(mSurface);
01052 
01053     mDC = mMainDC;
01054     mSurface = mMainSurface;
01055 
01056     //two pointers: two refs
01057     NS_IF_ADDREF(mSurface);
01058   }
01059 
01060   //release it...
01061   NS_IF_RELEASE(surf);
01062 
01063   return NS_OK;
01064 }
01065 
01066 NS_IMETHODIMP nsRenderingContextWin :: DrawLine(nscoord aX0, nscoord aY0, nscoord aX1, nscoord aY1)
01067 {
01068   if (nsLineStyle_kNone == mCurrLineStyle)
01069     return NS_OK;
01070 
01071        mTranMatrix->TransformCoord(&aX0,&aY0);
01072        mTranMatrix->TransformCoord(&aX1,&aY1);
01073 
01074   SetupPen();
01075 
01076 #ifndef WINCE
01077   if (nsLineStyle_kDotted == mCurrLineStyle)
01078   {
01079     lineddastruct dda_struct;
01080 
01081     dda_struct.nDottedPixel = 1;
01082     dda_struct.dc = mDC;
01083     dda_struct.crColor = mColor;
01084 
01085     LineDDA((int)(aX0),(int)(aY0),(int)(aX1),(int)(aY1),(LINEDDAPROC) LineDDAFunc,(long)&dda_struct);
01086   }
01087   else
01088 #endif
01089   {
01090     ::MoveToEx(mDC, (int)(aX0), (int)(aY0), NULL);
01091     ::LineTo(mDC, (int)(aX1), (int)(aY1));
01092   }
01093 
01094   return NS_OK;
01095 }
01096 
01097 
01098 NS_IMETHODIMP nsRenderingContextWin :: DrawPolyline(const nsPoint aPoints[], PRInt32 aNumPoints)
01099 {
01100   if (nsLineStyle_kNone == mCurrLineStyle)
01101     return NS_OK;
01102 
01103   // First transform nsPoint's into POINT's; perform coordinate space
01104   // transformation at the same time
01105   POINT pts[20];
01106   POINT* pp0 = pts;
01107 
01108   if (aNumPoints > 20)
01109     pp0 = new POINT[aNumPoints];
01110 
01111   POINT* pp = pp0;
01112   const nsPoint* np = &aPoints[0];
01113 
01114        for (PRInt32 i = 0; i < aNumPoints; i++, pp++, np++)
01115   {
01116               pp->x = np->x;
01117               pp->y = np->y;
01118               mTranMatrix->TransformCoord((int*)&pp->x,(int*)&pp->y);
01119        }
01120 
01121   // Draw the polyline
01122   SetupPen();
01123   ::Polyline(mDC, pp0, int(aNumPoints));
01124 
01125   // Release temporary storage if necessary
01126   if (pp0 != pts)
01127     delete [] pp0;
01128 
01129   return NS_OK;
01130 }
01131 
01132 NS_IMETHODIMP nsRenderingContextWin :: DrawRect(const nsRect& aRect)
01133 {
01134   RECT nr;
01135        nsRect tr;
01136 
01137        tr = aRect;
01138        mTranMatrix->TransformCoord(&tr.x,&tr.y,&tr.width,&tr.height);
01139        nr.left = tr.x;
01140        nr.top = tr.y;
01141        nr.right = tr.x+tr.width;
01142        nr.bottom = tr.y+tr.height;
01143 
01144   ::FrameRect(mDC, &nr, SetupSolidBrush());
01145 
01146   return NS_OK;
01147 }
01148 
01149 NS_IMETHODIMP nsRenderingContextWin :: DrawRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
01150 {
01151   RECT nr;
01152 
01153        mTranMatrix->TransformCoord(&aX,&aY,&aWidth,&aHeight);
01154        nr.left = aX;
01155        nr.top = aY;
01156        nr.right = aX+aWidth;
01157        nr.bottom = aY+aHeight;
01158 
01159   ::FrameRect(mDC, &nr, SetupSolidBrush());
01160 
01161   return NS_OK;
01162 }
01163 
01164 NS_IMETHODIMP nsRenderingContextWin :: FillRect(const nsRect& aRect)
01165 {
01166   RECT nr;
01167        nsRect tr;
01168 
01169        tr = aRect;
01170        mTranMatrix->TransformCoord(&tr.x,&tr.y,&tr.width,&tr.height);
01171   ConditionRect(tr, nr);
01172   ::FillRect(mDC, &nr, SetupSolidBrush());
01173 
01174   return NS_OK;
01175 }
01176 
01177 NS_IMETHODIMP nsRenderingContextWin :: FillRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
01178 {
01179   RECT nr;
01180        nsRect tr;
01181 
01182        mTranMatrix->TransformCoord(&aX,&aY,&aWidth,&aHeight);
01183        nr.left = aX;
01184        nr.top = aY;
01185        nr.right = aX+aWidth;
01186        nr.bottom = aY+aHeight;
01187 
01188   ::FillRect(mDC, &nr, SetupSolidBrush());
01189 
01190   return NS_OK;
01191 }
01192 
01193 NS_IMETHODIMP nsRenderingContextWin :: InvertRect(const nsRect& aRect)
01194 {
01195   RECT nr;
01196        nsRect tr;
01197 
01198        tr = aRect;
01199        mTranMatrix->TransformCoord(&tr.x,&tr.y,&tr.width,&tr.height);
01200   ConditionRect(tr, nr);
01201   ::InvertRect(mDC, &nr);
01202 
01203   return NS_OK;
01204 }
01205 
01206 NS_IMETHODIMP nsRenderingContextWin :: InvertRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
01207 {
01208   RECT nr;
01209        nsRect tr;
01210 
01211        mTranMatrix->TransformCoord(&aX,&aY,&aWidth,&aHeight);
01212        nr.left = aX;
01213        nr.top = aY;
01214        nr.right = aX+aWidth;
01215        nr.bottom = aY+aHeight;
01216 
01217   ::InvertRect(mDC, &nr);
01218 
01219   return NS_OK;
01220 }
01221 
01222 NS_IMETHODIMP nsRenderingContextWin :: DrawPolygon(const nsPoint aPoints[], PRInt32 aNumPoints)
01223 {
01224   // First transform nsPoint's into POINT's; perform coordinate space
01225   // transformation at the same time
01226   POINT pts[20];
01227   POINT* pp0 = pts;
01228 
01229   if (aNumPoints > 20)
01230     pp0 = new POINT[aNumPoints];
01231 
01232   POINT* pp = pp0;
01233   const nsPoint* np = &aPoints[0];
01234 
01235        for (PRInt32 i = 0; i < aNumPoints; i++, pp++, np++)
01236   {
01237               pp->x = np->x;
01238               pp->y = np->y;
01239               mTranMatrix->TransformCoord((int*)&pp->x,(int*)&pp->y);
01240        }
01241 
01242   // Outline the polygon - note we are implicitly ignoring the linestyle here
01243   SetupSolidPen();
01244   HBRUSH oldBrush = (HBRUSH)::SelectObject(mDC, ::GetStockObject(NULL_BRUSH));
01245   ::Polygon(mDC, pp0, int(aNumPoints));
01246   ::SelectObject(mDC, oldBrush);
01247 
01248   // Release temporary storage if necessary
01249   if (pp0 != pts)
01250     delete [] pp0;
01251 
01252   return NS_OK;
01253 }
01254 
01255 NS_IMETHODIMP nsRenderingContextWin :: FillPolygon(const nsPoint aPoints[], PRInt32 aNumPoints)
01256 {
01257   // First transform nsPoint's into POINT's; perform coordinate space
01258   // transformation at the same time
01259 
01260   POINT pts[20];
01261   POINT* pp0 = pts;
01262 
01263   if (aNumPoints > 20)
01264     pp0 = new POINT[aNumPoints];
01265 
01266   POINT* pp = pp0;
01267   const nsPoint* np = &aPoints[0];
01268 
01269        for (PRInt32 i = 0; i < aNumPoints; i++, pp++, np++)
01270        {
01271               pp->x = np->x;
01272               pp->y = np->y;
01273               mTranMatrix->TransformCoord((int*)&pp->x,(int*)&pp->y);
01274        }
01275 
01276   // Fill the polygon
01277   SetupSolidBrush();
01278 
01279   if (NULL == mNullPen)
01280     mNullPen = ::CreatePen(PS_NULL, 0, 0);
01281 
01282   HPEN oldPen = (HPEN)::SelectObject(mDC, mNullPen);
01283   ::Polygon(mDC, pp0, int(aNumPoints));
01284   ::SelectObject(mDC, oldPen);
01285 
01286   // Release temporary storage if necessary
01287   if (pp0 != pts)
01288     delete [] pp0;
01289 
01290   return NS_OK;
01291 }
01292 
01293 
01294 NS_IMETHODIMP nsRenderingContextWin :: DrawEllipse(const nsRect& aRect)
01295 {
01296   return DrawEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
01297 }
01298 
01299 NS_IMETHODIMP nsRenderingContextWin :: DrawEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
01300 {
01301   if (nsLineStyle_kNone == mCurrLineStyle)
01302     return NS_OK;
01303 
01304   mTranMatrix->TransformCoord(&aX, &aY, &aWidth, &aHeight);
01305 
01306   SetupPen();
01307 
01308   HBRUSH oldBrush = (HBRUSH)::SelectObject(mDC, ::GetStockObject(NULL_BRUSH));
01309   
01310   ::Ellipse(mDC, aX, aY, aX + aWidth, aY + aHeight);
01311   ::SelectObject(mDC, oldBrush);
01312 
01313   return NS_OK;
01314 }
01315 
01316 NS_IMETHODIMP nsRenderingContextWin :: FillEllipse(const nsRect& aRect)
01317 {
01318   return FillEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
01319 }
01320 
01321 NS_IMETHODIMP nsRenderingContextWin :: FillEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
01322 {
01323   mTranMatrix->TransformCoord(&aX, &aY, &aWidth, &aHeight);
01324 
01325   SetupSolidPen();
01326   SetupSolidBrush();
01327   
01328   ::Ellipse(mDC, aX, aY, aX + aWidth, aY + aHeight);
01329 
01330   return NS_OK;
01331 }
01332 
01333 NS_IMETHODIMP nsRenderingContextWin :: DrawArc(const nsRect& aRect,
01334                                  float aStartAngle, float aEndAngle)
01335 {
01336   return DrawArc(aRect.x,aRect.y,aRect.width,aRect.height,aStartAngle,aEndAngle);
01337 }
01338 
01339 NS_IMETHODIMP nsRenderingContextWin :: DrawArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
01340                                  float aStartAngle, float aEndAngle)
01341 {
01342   if (nsLineStyle_kNone == mCurrLineStyle)
01343     return NS_OK;
01344 
01345   PRInt32 quad1, quad2, sx, sy, ex, ey, cx, cy;
01346   float   anglerad, distance;
01347 
01348   mTranMatrix->TransformCoord(&aX, &aY, &aWidth, &aHeight);
01349 
01350   SetupPen();
01351   SetupSolidBrush();
01352 
01353   // figure out the the coordinates of the arc from the angle
01354   distance = (float)sqrt((float)(aWidth * aWidth + aHeight * aHeight));
01355   cx = aX + aWidth / 2;
01356   cy = aY + aHeight / 2;
01357 
01358   anglerad = (float)(aStartAngle / (180.0 / 3.14159265358979323846));
01359   quad1 = (PRInt32)(aStartAngle / 90.0);
01360   sx = (PRInt32)(distance * cos(anglerad) + cx);
01361   sy = (PRInt32)(cy - distance * sin(anglerad));
01362 
01363   anglerad = (float)(aEndAngle / (180.0 / 3.14159265358979323846));
01364   quad2 = (PRInt32)(aEndAngle / 90.0);
01365   ex = (PRInt32)(distance * cos(anglerad) + cx);
01366   ey = (PRInt32)(cy - distance * sin(anglerad));
01367 
01368   // this just makes it consitent, on windows 95 arc will always draw CC, nt this sets direction
01369 #ifndef WINCE
01370   ::SetArcDirection(mDC, AD_COUNTERCLOCKWISE);
01371 #endif
01372 
01373   ::Arc(mDC, aX, aY, aX + aWidth, aY + aHeight, sx, sy, ex, ey); 
01374 
01375   return NS_OK;
01376 }
01377 
01378 NS_IMETHODIMP nsRenderingContextWin :: FillArc(const nsRect& aRect,
01379                                  float aStartAngle, float aEndAngle)
01380 {
01381   return FillArc(aRect.x, aRect.y, aRect.width, aRect.height, aStartAngle, aEndAngle);
01382 }
01383 
01384 NS_IMETHODIMP nsRenderingContextWin :: FillArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
01385                                  float aStartAngle, float aEndAngle)
01386 {
01387   PRInt32 quad1, quad2, sx, sy, ex, ey, cx, cy;
01388   float   anglerad, distance;
01389 
01390   mTranMatrix->TransformCoord(&aX, &aY, &aWidth, &aHeight);
01391 
01392   SetupSolidPen();
01393   SetupSolidBrush();
01394 
01395   // figure out the the coordinates of the arc from the angle
01396   distance = (float)sqrt((float)(aWidth * aWidth + aHeight * aHeight));
01397   cx = aX + aWidth / 2;
01398   cy = aY + aHeight / 2;
01399 
01400   anglerad = (float)(aStartAngle / (180.0 / 3.14159265358979323846));
01401   quad1 = (PRInt32)(aStartAngle / 90.0);
01402   sx = (PRInt32)(distance * cos(anglerad) + cx);
01403   sy = (PRInt32)(cy - distance * sin(anglerad));
01404 
01405   anglerad = (float)(aEndAngle / (180.0 / 3.14159265358979323846));
01406   quad2 = (PRInt32)(aEndAngle / 90.0);
01407   ex = (PRInt32)(distance * cos(anglerad) + cx);
01408   ey = (PRInt32)(cy - distance * sin(anglerad));
01409 
01410   // this just makes it consistent, on windows 95 arc will always draw CC,
01411   // on NT this sets direction
01412 #ifndef WINCE
01413   ::SetArcDirection(mDC, AD_COUNTERCLOCKWISE);
01414 #endif
01415   ::Pie(mDC, aX, aY, aX + aWidth, aY + aHeight, sx, sy, ex, ey); 
01416 
01417   return NS_OK;
01418 }
01419 
01420 // This must be called to clamp string lengths to 8K for Win95/98/ME.
01421 inline void CheckLength(PRUint32 *aLength)
01422 {
01423   if (gIsWIN95 && *aLength > 8192)
01424     *aLength = 8192;
01425 }
01426 
01427 NS_IMETHODIMP nsRenderingContextWin :: GetWidth(char ch, nscoord& aWidth)
01428 {
01429   char buf[1];
01430   buf[0] = ch;
01431   return GetWidth(buf, 1, aWidth);
01432 }
01433 
01434 NS_IMETHODIMP nsRenderingContextWin :: GetWidth(PRUnichar ch, nscoord &aWidth, PRInt32 *aFontID)
01435 {
01436   PRUnichar buf[1];
01437   buf[0] = ch;
01438   return GetWidth(buf, 1, aWidth, aFontID);
01439 }
01440 
01441 NS_IMETHODIMP nsRenderingContextWin :: GetWidthInternal(const char* aString,
01442                                                         PRUint32 aLength,
01443                                                         nscoord& aWidth)
01444 {
01445 
01446   if (nsnull != mFontMetrics)
01447   {
01448     // Check for the very common case of trying to get the width of a single
01449     // space.
01450     if ((1 == aLength) && (aString[0] == ' '))
01451     {
01452       return mFontMetrics->GetSpaceWidth(aWidth);
01453     }
01454 
01455     CheckLength(&aLength);
01456     SetupFontAndColor();
01457     nscoord pxWidth = mCurrFontWin->GetWidth(mDC, aString, aLength);
01458     aWidth = NSToCoordRound(float(pxWidth) * mP2T);
01459 
01460     return NS_OK;
01461   }
01462   else
01463     return NS_ERROR_FAILURE;
01464 }
01465 
01466 struct GetWidthData {
01467   HDC   mDC;    // IN
01468   HFONT mFont;  // IN/OUT (running)
01469   LONG  mWidth; // IN/OUT (running, accumulated width so far)
01470 };
01471 
01472 static PRBool PR_CALLBACK
01473 do_GetWidth(const nsFontSwitch* aFontSwitch,
01474             const PRUnichar*    aSubstring,
01475             PRUint32            aSubstringLength,
01476             void*               aData)
01477 {
01478   nsFontWin* fontWin = aFontSwitch->mFontWin;
01479 
01480   GetWidthData* data = (GetWidthData*)aData;
01481   if (data->mFont != fontWin->mFont) {
01482     // the desired font is not the current font in the DC
01483     data->mFont = fontWin->mFont;
01484     ::SelectObject(data->mDC, data->mFont);
01485   }
01486   data->mWidth += fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength);
01487   return PR_TRUE; // don't stop till the end
01488 }
01489 
01490 NS_IMETHODIMP nsRenderingContextWin :: GetWidthInternal(const PRUnichar *aString,
01491                                                         PRUint32 aLength,
01492                                                         nscoord &aWidth,
01493                                                         PRInt32 *aFontID)
01494 {
01495   if (!mFontMetrics) return NS_ERROR_FAILURE;
01496 
01497   CheckLength(&aLength);
01498   SetupFontAndColor();
01499 
01500   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
01501   GetWidthData data = {mDC, mCurrFont, 0};
01502 
01503   metrics->ResolveForwards(mDC, aString, aLength, do_GetWidth, &data);
01504   aWidth = NSToCoordRound(float(data.mWidth) * mP2T);
01505 
01506   if (mCurrFont != data.mFont) {
01507     // If the font was changed along the way, restore our font
01508     ::SelectObject(mDC, mCurrFont);
01509   }
01510 
01511   if (aFontID) *aFontID = 0;
01512 
01513   return NS_OK;
01514 }
01515 
01516 NS_IMETHODIMP
01517 nsRenderingContextWin::GetTextDimensionsInternal(const char*       aString,
01518                                                  PRInt32           aLength,
01519                                                  PRInt32           aAvailWidth,
01520                                                  PRInt32*          aBreaks,
01521                                                  PRInt32           aNumBreaks,
01522                                                  nsTextDimensions& aDimensions,
01523                                                  PRInt32&          aNumCharsFit,
01524                                                  nsTextDimensions& aLastWordDimensions,
01525                                                  PRInt32*          aFontID)
01526 {
01527   NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array");
01528 
01529   if (nsnull != mFontMetrics) {
01530     // Setup the font and foreground color
01531     CheckLength((PRUint32*)&aLength);
01532     SetupFontAndColor();
01533 
01534     // If we need to back up this state represents the last place we could
01535     // break. We can use this to avoid remeasuring text
01536     PRInt32 prevBreakState_BreakIndex = -1; // not known (hasn't been computed)
01537     nscoord prevBreakState_Width = 0; // accumulated width to this point
01538 
01539     // Initialize OUT parameters
01540     mFontMetrics->GetMaxAscent(aLastWordDimensions.ascent);
01541     mFontMetrics->GetMaxDescent(aLastWordDimensions.descent);
01542     aLastWordDimensions.width = -1;
01543     aNumCharsFit = 0;
01544 
01545     // Iterate each character in the string and determine which font to use
01546     nscoord width = 0;
01547     PRInt32 start = 0;
01548     nscoord aveCharWidth;
01549     mFontMetrics->GetAveCharWidth(aveCharWidth);
01550 
01551     while (start < aLength) {
01552       // Estimate how many characters will fit. Do that by diving the available
01553       // space by the average character width. Make sure the estimated number
01554       // of characters is at least 1
01555       PRInt32 estimatedNumChars = 0;
01556       if (aveCharWidth > 0) {
01557         estimatedNumChars = (aAvailWidth - width) / aveCharWidth;
01558       }
01559       if (estimatedNumChars < 1) {
01560         estimatedNumChars = 1;
01561       }
01562 
01563       // Find the nearest break offset
01564       PRInt32 estimatedBreakOffset = start + estimatedNumChars;
01565       PRInt32 breakIndex;
01566       nscoord numChars;
01567 
01568       // Find the nearest place to break that is less than or equal to
01569       // the estimated break offset
01570       if (aLength <= estimatedBreakOffset) {
01571         // All the characters should fit
01572         numChars = aLength - start;
01573         breakIndex = aNumBreaks - 1;
01574       } 
01575       else {
01576         breakIndex = prevBreakState_BreakIndex;
01577         while (((breakIndex + 1) < aNumBreaks) &&
01578                (aBreaks[breakIndex + 1] <= estimatedBreakOffset)) {
01579           ++breakIndex;
01580         }
01581         if (breakIndex == prevBreakState_BreakIndex) {
01582           ++breakIndex; // make sure we advanced past the previous break index
01583         }
01584         numChars = aBreaks[breakIndex] - start;
01585       }
01586 
01587       // Measure the text
01588       nscoord pxWidth, twWidth = 0;
01589       if ((1 == numChars) && (aString[start] == ' ')) {
01590         mFontMetrics->GetSpaceWidth(twWidth);
01591       } 
01592       else if (numChars > 0) {
01593         pxWidth = mCurrFontWin->GetWidth(mDC, &aString[start], numChars);
01594         twWidth = NSToCoordRound(float(pxWidth) * mP2T);
01595       }
01596 
01597       // See if the text fits
01598       PRBool  textFits = (twWidth + width) <= aAvailWidth;
01599 
01600       // If the text fits then update the width and the number of
01601       // characters that fit
01602       if (textFits) {
01603         aNumCharsFit += numChars;
01604         width += twWidth;
01605         start += numChars;
01606 
01607         // This is a good spot to back up to if we need to so remember
01608         // this state
01609         prevBreakState_BreakIndex = breakIndex;
01610         prevBreakState_Width = width;
01611       }
01612       else {
01613         // See if we can just back up to the previous saved state and not
01614         // have to measure any text
01615         if (prevBreakState_BreakIndex > 0) {
01616           // If the previous break index is just before the current break index
01617           // then we can use it
01618           if (prevBreakState_BreakIndex == (breakIndex - 1)) {
01619             aNumCharsFit = aBreaks[prevBreakState_BreakIndex];
01620             width = prevBreakState_Width;
01621             break;
01622           }
01623         }
01624 
01625         // We can't just revert to the previous break state
01626         if (0 == breakIndex) {
01627           // There's no place to back up to, so even though the text doesn't fit
01628           // return it anyway
01629           aNumCharsFit += numChars;
01630           width += twWidth;
01631           break;
01632         }
01633 
01634         // Repeatedly back up until we get to where the text fits or we're all
01635         // the way back to the first word
01636         width += twWidth;
01637         while ((breakIndex >= 1) && (width > aAvailWidth)) {
01638           twWidth = 0;
01639           start = aBreaks[breakIndex - 1];
01640           numChars = aBreaks[breakIndex] - start;
01641           
01642           if ((1 == numChars) && (aString[start] == ' ')) {
01643             mFontMetrics->GetSpaceWidth(twWidth);
01644           } 
01645           else if (numChars > 0) {
01646             pxWidth = mCurrFontWin->GetWidth(mDC, &aString[start], numChars);
01647             twWidth = NSToCoordRound(float(pxWidth) * mP2T);
01648           }
01649 
01650           width -= twWidth;
01651           aNumCharsFit = start;
01652           breakIndex--;
01653         }
01654         break;
01655       }
01656     }
01657 
01658     aDimensions.width = width;
01659     mFontMetrics->GetMaxAscent(aDimensions.ascent);
01660     mFontMetrics->GetMaxDescent(aDimensions.descent);
01661 
01662     return NS_OK;
01663   }
01664 
01665   return NS_ERROR_FAILURE;
01666 }
01667 
01668 struct BreakGetTextDimensionsData {
01669   HDC      mDC;                // IN
01670   HFONT    mFont;              // IN/OUT (running)
01671   float    mP2T;               // IN
01672   PRInt32  mAvailWidth;        // IN
01673   PRInt32* mBreaks;            // IN
01674   PRInt32  mNumBreaks;         // IN
01675   nscoord  mSpaceWidth;        // IN
01676   nscoord  mAveCharWidth;      // IN
01677   PRInt32  mEstimatedNumChars; // IN (running -- to handle the edge case of one word)
01678 
01679   PRInt32  mNumCharsFit;  // IN/OUT -- accumulated number of chars that fit so far
01680   nscoord  mWidth;        // IN/OUT -- accumulated width so far
01681 
01682   // If we need to back up, this state represents the last place
01683   // we could break. We can use this to avoid remeasuring text
01684   PRInt32 mPrevBreakState_BreakIndex; // IN/OUT, initialized as -1, i.e., not yet computed
01685   nscoord mPrevBreakState_Width;      // IN/OUT, initialized as  0
01686 
01687   // Remember the fonts that we use so that we can deal with
01688   // line-breaking in-between fonts later. mOffsets[0] is also used
01689   // to initialize the current offset from where to start measuring
01690   nsVoidArray* mFonts;   // IN/OUT
01691   nsVoidArray* mOffsets; // IN/OUT
01692 
01693 };
01694 
01695 static PRBool PR_CALLBACK
01696 do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
01697                           const PRUnichar*    aSubstring,
01698                           PRUint32            aSubstringLength,
01699                           void*               aData)
01700 {
01701   nsFontWin* fontWin = aFontSwitch->mFontWin;
01702 
01703   // Make sure the font is selected
01704   BreakGetTextDimensionsData* data = (BreakGetTextDimensionsData*)aData;
01705   if (data->mFont != fontWin->mFont) {
01706     data->mFont = fontWin->mFont;
01707     ::SelectObject(data->mDC, data->mFont);
01708   }
01709 
01710   // Our current state relative to the _full_ string...
01711   // This allows emulation of the previous code...
01712   const PRUnichar* pstr = (const PRUnichar*)data->mOffsets->ElementAt(0);
01713   PRInt32 numCharsFit = data->mNumCharsFit;
01714   nscoord width = data->mWidth;
01715   PRInt32 start = (PRInt32)(aSubstring - pstr);
01716   PRInt32 end = start + aSubstringLength;
01717   PRBool allDone = PR_FALSE;
01718 
01719   while (start < end) {
01720     // Estimate how many characters will fit. Do that by dividing the
01721     // available space by the average character width
01722     PRInt32 estimatedNumChars = data->mEstimatedNumChars;
01723     if (!estimatedNumChars && data->mAveCharWidth > 0) {
01724       estimatedNumChars = (data->mAvailWidth - width) / data->mAveCharWidth;
01725     }
01726     // Make sure the estimated number of characters is at least 1
01727     if (estimatedNumChars < 1) {
01728       estimatedNumChars = 1;
01729     }
01730 
01731     // Find the nearest break offset
01732     PRInt32 estimatedBreakOffset = start + estimatedNumChars;
01733     PRInt32 breakIndex = -1; // not yet computed
01734     PRBool  inMiddleOfSegment = PR_FALSE;
01735     nscoord numChars;
01736 
01737     // Avoid scanning the break array in the case where we think all
01738     // the text should fit
01739     if (end <= estimatedBreakOffset) {
01740       // Everything should fit
01741       numChars = end - start;
01742     }
01743     else {
01744       // Find the nearest place to break that is less than or equal to
01745       // the estimated break offset
01746       breakIndex = data->mPrevBreakState_BreakIndex;
01747       while (breakIndex + 1 < data->mNumBreaks &&
01748              data->mBreaks[breakIndex + 1] <= estimatedBreakOffset) {
01749         ++breakIndex;
01750       }
01751 
01752       if (breakIndex == -1)
01753         breakIndex = 0;
01754 
01755       // We found a place to break that is before the estimated break
01756       // offset. Where we break depends on whether the text crosses a
01757       // segment boundary
01758       if (start < data->mBreaks[breakIndex]) {
01759         // The text crosses at least one segment boundary so measure to the
01760         // break point just before the estimated break offset
01761         numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
01762       } 
01763       else {
01764         // See whether there is another segment boundary between this one
01765         // and the end of the text
01766         if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < end)) {
01767           ++breakIndex;
01768           numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
01769         }
01770         else {
01771           NS_ASSERTION(end != data->mBreaks[breakIndex], "don't expect to be at segment boundary");
01772 
01773           // The text is all within the same segment
01774           numChars = end - start;
01775 
01776           // Remember we're in the middle of a segment and not between
01777           // two segments
01778           inMiddleOfSegment = PR_TRUE;
01779         }
01780       }
01781     }
01782 
01783     // Measure the text
01784     nscoord twWidth, pxWidth;
01785     if ((1 == numChars) && (pstr[start] == ' ')) {
01786       twWidth = data->mSpaceWidth;
01787     }
01788     else {
01789       pxWidth = fontWin->GetWidth(data->mDC, &pstr[start], numChars);
01790       twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
01791     }
01792 
01793     // See if the text fits
01794     PRBool textFits = (twWidth + width) <= data->mAvailWidth;
01795 
01796     // If the text fits then update the width and the number of
01797     // characters that fit
01798     if (textFits) {
01799       numCharsFit += numChars;
01800       width += twWidth;
01801 
01802       // If we computed the break index and we're not in the middle
01803       // of a segment then this is a spot that we can back up to if
01804       // we need to, so remember this state
01805       if ((breakIndex != -1) && !inMiddleOfSegment) {
01806         data->mPrevBreakState_BreakIndex = breakIndex;
01807         data->mPrevBreakState_Width = width;
01808       }
01809     }
01810     else {
01811       // The text didn't fit. If we're out of room then we're all done
01812       allDone = PR_TRUE;
01813 
01814       // See if we can just back up to the previous saved state and not
01815       // have to measure any text
01816       if (data->mPrevBreakState_BreakIndex != -1) {
01817         PRBool canBackup;
01818 
01819         // If we're in the middle of a word then the break index
01820         // must be the same if we can use it. If we're at a segment
01821         // boundary, then if the saved state is for the previous
01822         // break index then we can use it
01823         if (inMiddleOfSegment) {
01824           canBackup = data->mPrevBreakState_BreakIndex == breakIndex;
01825         } else {
01826           canBackup = data->mPrevBreakState_BreakIndex == (breakIndex - 1);
01827         }
01828 
01829         if (canBackup) {
01830           numCharsFit = data->mBreaks[data->mPrevBreakState_BreakIndex];
01831           width = data->mPrevBreakState_Width;
01832           break;
01833         }
01834       }
01835 
01836       // We can't just revert to the previous break state. Find the break
01837       // index just before the end of the text
01838       end = start + numChars;
01839       breakIndex = 0;
01840       if (data->mBreaks[breakIndex] < end) {
01841         while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < end)) {
01842           ++breakIndex;
01843         }
01844       }
01845 
01846       if ((0 == breakIndex) && (end <= data->mBreaks[0])) {
01847         // There's no place to back up to, so even though the text doesn't fit
01848         // return it anyway
01849         numCharsFit += numChars;
01850         width += twWidth;
01851 
01852         // Edge case of one word: it could be that we just measured a fragment of the
01853         // first word and its remainder involves other fonts, so we want to keep going
01854         // until we at least measure the entire first word
01855         if (numCharsFit < data->mBreaks[0]) {
01856           allDone = PR_FALSE;
01857           // From now on we don't care anymore what is the _real_ estimated
01858           // number of characters that fits. Rather, we have no where to break
01859           // and have to measure one word fully, but the real estimate is less
01860           // than that one word. However, since the other bits of code rely on
01861           // what is in "data->mEstimatedNumChars", we want to override
01862           // "data->mEstimatedNumChars" and pass in what _has_ to be measured
01863           // so that it is transparent to the other bits that depend on it.
01864           data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit;
01865           start += numChars;
01866         }
01867 
01868         break;
01869       }
01870 
01871       // Repeatedly back up until we get to where the text fits or we're
01872       // all the way back to the first word
01873       width += twWidth;
01874       while ((breakIndex >= 0) && (width > data->mAvailWidth)) {
01875         start = data->mBreaks[breakIndex];
01876         numChars = end - start;
01877         numCharsFit = start;
01878         if ((1 == numChars) && (pstr[start] == ' ')) {
01879           width -= data->mSpaceWidth;
01880         }
01881         else if (pstr + start >= aSubstring) {
01882           // The entire fragment to chop is within the current font.
01883           pxWidth = fontWin->GetWidth(data->mDC, &pstr[start], numChars);
01884           width -= NSToCoordRound(float(pxWidth) * data->mP2T);
01885         }
01886         else {
01887           // The fragment that we want to chop extends back into previous fonts.
01888           // We need to reverse into previous fonts. Fortunately,
01889           // data->mFonts[] and data->mOffsets[] tell us which fonts are used
01890           // and when. 
01891           end = data->mNumCharsFit; // same as aSubstring - pstr
01892           data->mNumCharsFit = numCharsFit; // has got shorter...
01893           PRInt32 k = data->mFonts->Count() - 1;
01894           for ( ; k >= 0 && start < end; --k, end -= numChars) {
01895             fontWin = (nsFontWin*)data->mFonts->ElementAt(k);
01896             const PRUnichar* ps = (const PRUnichar*)data->mOffsets->ElementAt(k);
01897             if (ps < pstr + start)
01898               ps = pstr + start;
01899 
01900             numChars = pstr + end - ps;
01901             NS_ASSERTION(numChars > 0, "empty string");
01902 
01903             data->mFont = fontWin->mFont;
01904             ::SelectObject(data->mDC, data->mFont);
01905             pxWidth = fontWin->GetWidth(data->mDC, ps, numChars);
01906             data->mWidth -= NSToCoordRound(float(pxWidth) * data->mP2T);
01907 
01908             // By construction, mFonts[k] is the last font, and
01909             // mOffsets[k+1] is the last offset.
01910             data->mFonts->RemoveElementAt(k);
01911             data->mOffsets->RemoveElementAt(k+1);
01912           }
01913 
01914           // We are done, update the data now because we won't do it later.
01915           // The |if (data->mNumCharsFit != numCharsFit)| won't apply below
01916           data->mFonts->AppendElement(fontWin);
01917           data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
01918           break;
01919         }
01920 
01921         --breakIndex;
01922         end = start;
01923       }
01924     }
01925 
01926     start += numChars;
01927   }
01928 
01929 #ifdef DEBUG_rbs
01930   NS_ASSERTION(allDone || start == end, "internal error");
01931   NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error");
01932 #endif /* DEBUG_rbs */
01933 
01934   if (data->mNumCharsFit != numCharsFit) {
01935     // some text was actually retained
01936     data->mWidth = width;
01937     data->mNumCharsFit = numCharsFit;
01938     data->mFonts->AppendElement(fontWin);
01939     data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
01940   }
01941 
01942   if (allDone) {
01943     // stop now
01944     return PR_FALSE;
01945   }
01946 
01947   return PR_TRUE; // don't stop if we still need to measure more characters
01948 }
01949 
01950 NS_IMETHODIMP
01951 nsRenderingContextWin::GetTextDimensionsInternal(const PRUnichar*  aString,
01952                                                  PRInt32           aLength,
01953                                                  PRInt32           aAvailWidth,
01954                                                  PRInt32*          aBreaks,
01955                                                  PRInt32           aNumBreaks,
01956                                                  nsTextDimensions& aDimensions,
01957                                                  PRInt32&          aNumCharsFit,
01958                                                  nsTextDimensions& aLastWordDimensions,
01959                                                  PRInt32*          aFontID)
01960 {
01961   if (!mFontMetrics) return NS_ERROR_FAILURE;
01962 
01963   CheckLength((PRUint32*)&aLength);
01964   SetupFontAndColor();
01965 
01966   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
01967 
01968   nscoord spaceWidth, aveCharWidth;
01969   metrics->GetSpaceWidth(spaceWidth);
01970   metrics->GetAveCharWidth(aveCharWidth);
01971 
01972   // Note: aBreaks[] is supplied to us so that the first word is located
01973   // at aString[0 .. aBreaks[0]-1] and more generally, the k-th word is
01974   // located at aString[aBreaks[k-1] .. aBreaks[k]-1]. Whitespace can
01975   // be included and each of them counts as a word in its own right.
01976 
01977   // Upon completion of glyph resolution, characters that can be
01978   // represented with fonts[i] are at offsets[i] .. offsets[i+1]-1
01979 
01980   nsAutoVoidArray fonts, offsets;
01981   offsets.AppendElement((void*)aString);
01982 
01983   BreakGetTextDimensionsData data = {mDC, mCurrFont, mP2T, 
01984     aAvailWidth, aBreaks, aNumBreaks, spaceWidth, aveCharWidth,
01985     0, 0, 0, -1, 0, &fonts, &offsets 
01986   };
01987 
01988   metrics->ResolveForwards(mDC, aString, aLength, do_BreakGetTextDimensions, &data);
01989 
01990   if (mCurrFont != data.mFont) {
01991     // If the font was changed along the way, restore our font
01992     ::SelectObject(mDC, mCurrFont);
01993   }
01994 
01995   if (aFontID) *aFontID = 0;
01996 
01997   aNumCharsFit = data.mNumCharsFit;
01998   aDimensions.width = data.mWidth;
01999 
02001   // Post-processing for the ascent and descent:
02002   //
02003   // The width of the last word is included in the final width, but its
02004   // ascent and descent are kept aside for the moment. The problem is that
02005   // line-breaking may occur _before_ the last word, and we don't want its
02006   // ascent and descent to interfere. We can re-measure the last word and
02007   // substract its width later. However, we need a special care for the ascent
02008   // and descent at the break-point. The idea is to keep the ascent and descent
02009   // of the last word separate, and let layout consider them later when it has
02010   // determined that line-breaking doesn't occur before the last word.
02011   //
02012   // Therefore, there are two things to do:
02013   // 1. Determine the ascent and descent up to where line-breaking may occur.
02014   // 2. Determine the ascent and descent of the remainder.
02015   //    For efficiency however, it is okay to bail out early if there is only
02016   //    one font (in this case, the height of the last word has no special
02017   //    effect on the total height).
02018 
02019   // aLastWordDimensions.width should be set to -1 to reply that we don't
02020   // know the width of the last word since we measure multiple words
02021   aLastWordDimensions.Clear();
02022   aLastWordDimensions.width = -1;
02023 
02024   PRInt32 count = fonts.Count();
02025   if (!count)
02026     return NS_OK;
02027   nsFontWin* fontWin = (nsFontWin*)fonts[0];
02028   NS_ASSERTION(fontWin, "internal error in do_BreakGetTextDimensions");
02029   aDimensions.ascent = fontWin->mMaxAscent;
02030   aDimensions.descent = fontWin->mMaxDescent;
02031 
02032   // fast path - normal case, quick return if there is only one font
02033   if (count == 1)
02034     return NS_OK;
02035 
02036   // get the last break index.
02037   // If there is only one word, we end up with lastBreakIndex = 0. We don't
02038   // need to worry about aLastWordDimensions in this case too. But if we didn't
02039   // return earlier, it would mean that the unique word needs several fonts
02040   // and we will still have to loop over the fonts to return the final height
02041   PRInt32 lastBreakIndex = 0;
02042   while (aBreaks[lastBreakIndex] < aNumCharsFit)
02043     ++lastBreakIndex;
02044 
02045   const PRUnichar* lastWord = (lastBreakIndex > 0) 
02046     ? aString + aBreaks[lastBreakIndex-1]
02047     : aString + aNumCharsFit; // let it point outside to play nice with the loop
02048 
02049   // now get the desired ascent and descent information... this is however
02050   // a very fast loop of the order of the number of additional fonts
02051 
02052   PRInt32 currFont = 0;
02053   const PRUnichar* pstr = aString;
02054   const PRUnichar* last = aString + aNumCharsFit;
02055 
02056   while (pstr < last) {
02057     fontWin = (nsFontWin*)fonts[currFont];
02058     PRUnichar* nextOffset = (PRUnichar*)offsets[++currFont]; 
02059 
02060     // For consistent word-wrapping, we are going to handle the whitespace
02061     // character with special care because a whitespace character can come
02062     // from a font different from that of the previous word. If 'x', 'y', 'z',
02063     // are Unicode points that require different fonts, we want 'xyz <br>'
02064     // and 'xyz<br>' to have the same height because it gives a more stable
02065     // rendering, especially when the window is resized at the edge of the word.
02066     // If we don't do this, a 'tall' trailing whitespace, i.e., if the whitespace
02067     // happens to come from a font with a bigger ascent and/or descent than all
02068     // current fonts on the line, this can cause the next lines to be shifted
02069     // down when the window is slowly resized to fit that whitespace.
02070     if (*pstr == ' ') {
02071       // skip pass the whitespace to ignore the height that it may contribute
02072       ++pstr;
02073       // get out if we reached the end
02074       if (pstr == last) {
02075         break;
02076       }
02077       // switch to the next font if we just passed the current font 
02078       if (pstr == nextOffset) {
02079         fontWin = (nsFontWin*)fonts[currFont];
02080         nextOffset = (PRUnichar*)offsets[++currFont];
02081       } 
02082     }
02083 
02084     // see if the last word intersects with the current font
02085     // (we are testing for 'nextOffset-1 >= lastWord' since the
02086     // current font ends at nextOffset-1)
02087     if (nextOffset > lastWord) {
02088       if (aLastWordDimensions.ascent < fontWin->mMaxAscent) {
02089         aLastWordDimensions.ascent = fontWin->mMaxAscent;
02090       }
02091       if (aLastWordDimensions.descent < fontWin->mMaxDescent) {
02092         aLastWordDimensions.descent = fontWin->mMaxDescent;
02093       }
02094     }
02095 
02096     // see if we have not reached the last word yet
02097     if (pstr < lastWord) {
02098       if (aDimensions.ascent < fontWin->mMaxAscent) {
02099         aDimensions.ascent = fontWin->mMaxAscent;
02100       }
02101       if (aDimensions.descent < fontWin->mMaxDescent) {
02102         aDimensions.descent = fontWin->mMaxDescent;
02103       }
02104     }
02105 
02106     // advance to where the next font starts
02107     pstr = nextOffset;
02108   }
02109 
02110   return NS_OK;
02111 }
02112 
02113 NS_IMETHODIMP
02114 nsRenderingContextWin::GetTextDimensionsInternal(const char*       aString,
02115                                                  PRUint32          aLength,
02116                                                  nsTextDimensions& aDimensions)
02117 {
02118   if (!mFontMetrics) {
02119     aDimensions.Clear();
02120     return NS_ERROR_FAILURE;
02121   }
02122   GetWidth(aString, aLength, aDimensions.width);
02123   mFontMetrics->GetMaxAscent(aDimensions.ascent);
02124   mFontMetrics->GetMaxDescent(aDimensions.descent);
02125   return NS_OK;
02126 }
02127 
02128 struct GetTextDimensionsData {
02129   HDC     mDC;      // IN
02130   HFONT   mFont;    // IN/OUT (running)
02131   LONG    mWidth;   // IN/OUT (running)
02132   nscoord mAscent;  // IN/OUT (running)
02133   nscoord mDescent; // IN/OUT (running)
02134 };
02135 
02136 static PRBool PR_CALLBACK
02137 do_GetTextDimensions(const nsFontSwitch* aFontSwitch,
02138                      const PRUnichar*    aSubstring,
02139                      PRUint32            aSubstringLength,
02140                      void*               aData)
02141 {
02142   nsFontWin* fontWin = aFontSwitch->mFontWin;
02143   GetTextDimensionsData* data = (GetTextDimensionsData*)aData;
02144   if (data->mFont != fontWin->mFont) {
02145     data->mFont = fontWin->mFont;
02146     ::SelectObject(data->mDC, data->mFont);
02147   }
02148   data->mWidth += fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength);
02149   if (data->mAscent < fontWin->mMaxAscent) {
02150     data->mAscent = fontWin->mMaxAscent;
02151   }
02152   if (data->mDescent < fontWin->mMaxDescent) {
02153     data->mDescent = fontWin->mMaxDescent;
02154   }
02155 
02156   return PR_TRUE; // don't stop till the end
02157 }
02158 
02159 NS_IMETHODIMP
02160 nsRenderingContextWin::GetTextDimensionsInternal(const PRUnichar*  aString,
02161                                                  PRUint32          aLength,
02162                                                  nsTextDimensions& aDimensions,
02163                                                  PRInt32*          aFontID)
02164 {
02165   aDimensions.Clear();
02166   if (!mFontMetrics) return NS_ERROR_FAILURE;
02167 
02168   CheckLength(&aLength);
02169   SetupFontAndColor();
02170 
02171   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
02172   GetTextDimensionsData data = {mDC, mCurrFont, 0, 0, 0};
02173 
02174   metrics->ResolveForwards(mDC, aString, aLength, do_GetTextDimensions, &data);
02175   aDimensions.width = NSToCoordRound(float(data.mWidth) * mP2T);
02176   aDimensions.ascent = data.mAscent;
02177   aDimensions.descent = data.mDescent;
02178 
02179   if (mCurrFont != data.mFont) {
02180     // If the font was changed on the way, restore our font
02181     ::SelectObject(mDC, mCurrFont);
02182   }
02183 
02184   if (aFontID) *aFontID = 0;
02185 
02186   return NS_OK;
02187 }
02188 
02189 NS_IMETHODIMP nsRenderingContextWin :: DrawStringInternal(const char *aString, PRUint32 aLength,
02190                                                           nscoord aX, nscoord aY,
02191                                                           const nscoord* aSpacing)
02192 {
02193   NS_PRECONDITION(mFontMetrics,"Something is wrong somewhere");
02194 
02195   PRInt32 x = aX;
02196   PRInt32 y = aY;
02197 
02198   CheckLength(&aLength);
02199   SetupFontAndColor();
02200 
02201   INT dxMem[500];
02202   INT* dx0 = NULL;
02203   if (aSpacing) {
02204     dx0 = dxMem;
02205     if (aLength > 500) {
02206       dx0 = new INT[aLength];
02207     }
02208     mTranMatrix->ScaleXCoords(aSpacing, aLength, dx0);
02209   }
02210   mTranMatrix->TransformCoord(&x, &y);
02211 
02212   mCurrFontWin->DrawString(mDC, x, y, aString, aLength, dx0);
02213 
02214   if (dx0 && (dx0 != dxMem)) {
02215     delete [] dx0;
02216   }
02217 
02218   return NS_OK;
02219 }
02220 
02221 struct DrawStringData {
02222   HDC            mDC;         // IN
02223   HFONT          mFont;       // IN/OUT (running)
02224   nsTransform2D* mTranMatrix; // IN
02225   nscoord        mX;          // IN/OUT (running)
02226   nscoord        mY;          // IN
02227   const nscoord* mSpacing;    // IN
02228   nscoord        mMaxLength;  // IN (length of the full string)
02229   nscoord        mLength;     // IN/OUT (running, current length already rendered)
02230 };
02231 
02232 static PRBool PR_CALLBACK
02233 do_DrawString(const nsFontSwitch* aFontSwitch,
02234               const PRUnichar*    aSubstring,
02235               PRUint32            aSubstringLength,
02236               void*               aData)
02237 {
02238   nsFontWin* fontWin = aFontSwitch->mFontWin;
02239 
02240   PRInt32 x, y;
02241   DrawStringData* data = (DrawStringData*)aData;
02242   if (data->mFont != fontWin->mFont) {
02243     data->mFont = fontWin->mFont;
02244     ::SelectObject(data->mDC, data->mFont);
02245   }
02246 
02247   data->mLength += aSubstringLength;
02248   if (data->mSpacing) {
02249     // XXX Fix path to use a twips transform in the DC and use the
02250     // spacing values directly and let windows deal with the sub-pixel
02251     // positioning.
02252 
02253     // Slow, but accurate rendering
02254     const PRUnichar* str = aSubstring;
02255     const PRUnichar* end = aSubstring + aSubstringLength;
02256     while (str < end) {
02257       // XXX can shave some cycles by inlining a version of transform
02258       // coord where y is constant and transformed once
02259       x = data->mX;
02260       y = data->mY;
02261       data->mTranMatrix->TransformCoord(&x, &y);
02262       if (IS_HIGH_SURROGATE(*str) && 
02263           ((str+1)<end) && 
02264           IS_LOW_SURROGATE(*(str+1))) 
02265       {
02266         // special case for surrogate pair
02267         fontWin->DrawString(data->mDC, x, y, str, 2);
02268         // we need to advance data->mX and str twice
02269         data->mX += *data->mSpacing++;
02270         ++str;
02271       } else {
02272         fontWin->DrawString(data->mDC, x, y, str, 1);
02273       }
02274       data->mX += *data->mSpacing++;
02275       ++str;
02276     }
02277   }
02278   else {
02279     fontWin->DrawString(data->mDC, data->mX, data->mY, aSubstring, aSubstringLength);
02280     // be ready if there is more to come
02281     if (data->mLength < data->mMaxLength) {
02282       data->mX += fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength);
02283     }
02284   }
02285   return PR_TRUE; // don't stop till the end
02286 }
02287 
02288 NS_IMETHODIMP nsRenderingContextWin :: DrawStringInternal(const PRUnichar *aString, PRUint32 aLength,
02289                                                           nscoord aX, nscoord aY,
02290                                                           PRInt32 aFontID,
02291                                                           const nscoord* aSpacing)
02292 {
02293   if (!mFontMetrics) return NS_ERROR_FAILURE;
02294 
02295   CheckLength(&aLength);
02296   SetupFontAndColor();
02297 
02298   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
02299   DrawStringData data = {mDC, mCurrFont, mTranMatrix, 
02300     aX, aY, aSpacing, aLength, 0
02301   };
02302   if (!aSpacing) { // @see do_DrawString for the spacing case
02303     mTranMatrix->TransformCoord(&data.mX, &data.mY);
02304   }
02305 
02306   if (mRightToLeftText) {
02307     metrics->ResolveBackwards(mDC, aString, aLength, do_DrawString, &data);
02308   }
02309   else
02310   {
02311     metrics->ResolveForwards(mDC, aString, aLength, do_DrawString, &data);
02312   }
02313 
02314   if (mCurrFont != data.mFont) {
02315     // If the font was changed along the way, restore our font
02316     ::SelectObject(mDC, mCurrFont);
02317   }
02318 
02319   return NS_OK;
02320 }
02321 
02322 #ifdef MOZ_MATHML
02323 NS_IMETHODIMP 
02324 nsRenderingContextWin::GetBoundingMetricsInternal(const char*        aString,
02325                                                   PRUint32           aLength,
02326                                                   nsBoundingMetrics& aBoundingMetrics)
02327 {
02328   NS_PRECONDITION(mFontMetrics,"Something is wrong somewhere");
02329 
02330   aBoundingMetrics.Clear();
02331   if (!mFontMetrics) return NS_ERROR_FAILURE;
02332 
02333   CheckLength(&aLength);
02334   SetupFontAndColor();
02335   nsresult rv = mCurrFontWin->GetBoundingMetrics(mDC, aString, aLength, aBoundingMetrics);
02336 
02337   // convert to app units
02338   aBoundingMetrics.leftBearing = NSToCoordRound(float(aBoundingMetrics.leftBearing) * mP2T);
02339   aBoundingMetrics.rightBearing = NSToCoordRound(float(aBoundingMetrics.rightBearing) * mP2T);
02340   aBoundingMetrics.width = NSToCoordRound(float(aBoundingMetrics.width) * mP2T);
02341   aBoundingMetrics.ascent = NSToCoordRound(float(aBoundingMetrics.ascent) * mP2T);
02342   aBoundingMetrics.descent = NSToCoordRound(float(aBoundingMetrics.descent) * mP2T);
02343 
02344   return rv;
02345 }
02346 
02347 struct GetBoundingMetricsData {
02348   HDC                mDC;              // IN
02349   HFONT              mFont;            // IN/OUT (running)
02350   nsBoundingMetrics* mBoundingMetrics; // IN/OUT (running)
02351   PRBool             mFirstTime;       // IN/OUT (set once)
02352   nsresult           mStatus;          // OUT
02353 };
02354 
02355 static PRBool PR_CALLBACK
02356 do_GetBoundingMetrics(const nsFontSwitch* aFontSwitch,
02357                       const PRUnichar*    aSubstring,
02358                       PRUint32            aSubstringLength,
02359                       void*               aData)
02360 {
02361   nsFontWin* fontWin = aFontSwitch->mFontWin;
02362 
02363   GetBoundingMetricsData* data = (GetBoundingMetricsData*)aData;
02364   if (data->mFont != fontWin->mFont) {
02365     data->mFont = fontWin->mFont;
02366     ::SelectObject(data->mDC, data->mFont);
02367   }
02368 
02369   nsBoundingMetrics rawbm;
02370   data->mStatus = fontWin->GetBoundingMetrics(data->mDC, aSubstring, aSubstringLength, rawbm);
02371   if (NS_FAILED(data->mStatus)) {
02372     return PR_FALSE; // stop now
02373   }
02374 
02375   if (data->mFirstTime) {
02376     data->mFirstTime = PR_FALSE;
02377     *data->mBoundingMetrics = rawbm;
02378   }
02379   else {
02380     *data->mBoundingMetrics += rawbm;
02381   }
02382 
02383   return PR_TRUE; // don't stop till the end
02384 }
02385 
02386 NS_IMETHODIMP
02387 nsRenderingContextWin::GetBoundingMetricsInternal(const PRUnichar*   aString,
02388                                                   PRUint32           aLength,
02389                                                   nsBoundingMetrics& aBoundingMetrics,
02390                                                   PRInt32*           aFontID)
02391 {
02392   aBoundingMetrics.Clear();
02393   if (!mFontMetrics) return NS_ERROR_FAILURE;
02394 
02395   CheckLength(&aLength);
02396   SetupFontAndColor();
02397 
02398   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
02399   GetBoundingMetricsData data = {mDC, mCurrFont, &aBoundingMetrics, PR_TRUE, NS_OK};
02400 
02401   nsresult rv = metrics->ResolveForwards(mDC, aString, aLength, do_GetBoundingMetrics, &data);
02402   if (NS_SUCCEEDED(rv)) {
02403     rv = data.mStatus;
02404   }
02405 
02406   // convert to app units
02407   aBoundingMetrics.leftBearing = NSToCoordRound(float(aBoundingMetrics.leftBearing) * mP2T);
02408   aBoundingMetrics.rightBearing = NSToCoordRound(float(aBoundingMetrics.rightBearing) * mP2T);
02409   aBoundingMetrics.width = NSToCoordRound(float(aBoundingMetrics.width) * mP2T);
02410   aBoundingMetrics.ascent = NSToCoordRound(float(aBoundingMetrics.ascent) * mP2T);
02411   aBoundingMetrics.descent = NSToCoordRound(float(aBoundingMetrics.descent) * mP2T);
02412 
02413   if (mCurrFont != data.mFont) {
02414     // If the font was changed along the way, restore our font
02415     ::SelectObject(mDC, mCurrFont);
02416   }
02417 
02418   if (aFontID) *aFontID = 0;
02419 
02420   return rv;
02421 }
02422 #endif // MOZ_MATHML
02423 
02424 PRInt32 nsRenderingContextWin::GetMaxStringLength()
02425 {
02426   if (!mFontMetrics)
02427     return 1;
02428   return NS_STATIC_CAST(nsFontMetricsWin*, mFontMetrics)->GetMaxStringLength();
02429 }
02430 
02431 NS_IMETHODIMP nsRenderingContextWin :: CopyOffScreenBits(nsIDrawingSurface* aSrcSurf,
02432                                                          PRInt32 aSrcX, PRInt32 aSrcY,
02433                                                          const nsRect &aDestBounds,
02434                                                          PRUint32 aCopyFlags)
02435 {
02436 
02437   if ((nsnull != aSrcSurf) && (nsnull != mMainDC))
02438   {
02439     PRInt32 x = aSrcX;
02440     PRInt32 y = aSrcY;
02441     nsRect  drect = aDestBounds;
02442     HDC     destdc, srcdc;
02443 
02444     //get back a DC
02445 
02446     ((nsDrawingSurfaceWin *)aSrcSurf)->GetDC(&srcdc);
02447 
02448     if (nsnull != srcdc)
02449     {
02450       if (aCopyFlags & NS_COPYBITS_TO_BACK_BUFFER)
02451       {
02452         NS_ASSERTION(!(nsnull == mDC), "no back buffer");
02453         destdc = mDC;
02454       }
02455       else
02456         destdc = mMainDC;
02457 
02458       if (aCopyFlags & NS_COPYBITS_USE_SOURCE_CLIP_REGION)
02459       {
02460         HRGN  tregion = ::CreateRectRgn(0, 0, 0, 0);
02461 
02462         if (::GetClipRgn(srcdc, tregion) == 1)
02463           ::SelectClipRgn(destdc, tregion);
02464 
02465         ::DeleteObject(tregion);
02466       }
02467 
02468       // If there's a palette make sure it's selected.
02469       // XXX This doesn't seem like the best place to be doing this...
02470 
02471       nsPaletteInfo palInfo;
02472 
02473       mContext->GetPaletteInfo(palInfo);
02474 
02475       if (palInfo.isPaletteDevice && palInfo.palette){
02476         ::SelectPalette(destdc, (HPALETTE)palInfo.palette, PR_TRUE);
02477         // this is called to many times here.. taking this out because it breaks
02478         // embedding.. its like fighting palettes.  All the palette stuff should
02479         // be taken out.. but its so late in the beta cycle... I am taking the safe
02480         // road until I can get all this figured out.. and completed correctly.
02481         // Opened bug #153367 to take care of this palette issue.
02482         //::RealizePalette(destdc);
02483 #ifndef WINCE
02484         ::UpdateColors(mDC);                                                      
02485 #endif
02486       }
02487 
02488       if (aCopyFlags & NS_COPYBITS_XFORM_SOURCE_VALUES)
02489         mTranMatrix->TransformCoord(&x, &y);
02490 
02491       if (aCopyFlags & NS_COPYBITS_XFORM_DEST_VALUES)
02492         mTranMatrix->TransformCoord(&drect.x, &drect.y, &drect.width, &drect.height);
02493 
02494       ::BitBlt(destdc, drect.x, drect.y,
02495                drect.width, drect.height,
02496                srcdc, x, y, SRCCOPY);
02497 
02498 
02499       //kill the DC
02500       ((nsDrawingSurfaceWin *)aSrcSurf)->ReleaseDC();
02501     }
02502     else
02503       NS_ASSERTION(0, "attempt to blit with bad DCs");
02504   }
02505   else
02506     NS_ASSERTION(0, "attempt to blit with bad DCs");
02507 
02508   return NS_OK;
02509 }
02510 
02511 //~~~
02512 NS_IMETHODIMP nsRenderingContextWin::RetrieveCurrentNativeGraphicData(void** ngd)
02513 {
02514   if(ngd != nsnull)
02515     *ngd = (void*)mDC;
02516   return NS_OK;
02517 }
02518 
02519 // Small cache of HBRUSH objects
02520 // Note: the current assumption is that there is only one UI thread so
02521 // we do not lock, and we do not use TLS
02522 static const int  BRUSH_CACHE_SIZE = 17;  // 2 stock plus 15
02523 
02524 class SolidBrushCache {
02525 public:
02526   SolidBrushCache();
02527   ~SolidBrushCache();
02528 
02529   HBRUSH  GetSolidBrush(HDC theHDC, COLORREF aColor);
02530 
02531 private:
02532   struct CacheEntry {
02533     HBRUSH   mBrush;
02534     COLORREF mBrushColor;
02535   };
02536 
02537   CacheEntry  mCache[BRUSH_CACHE_SIZE];
02538   int         mIndexOldest;  // index of oldest entry in cache
02539 };
02540 
02541 SolidBrushCache::SolidBrushCache()
02542   : mIndexOldest(2)
02543 {
02544   // First two entries are stock objects
02545   mCache[0].mBrush = gStockWhiteBrush;
02546   mCache[0].mBrushColor = RGB(255, 255, 255);
02547   mCache[1].mBrush = (HBRUSH)::GetStockObject(BLACK_BRUSH);
02548   mCache[1].mBrushColor = RGB(0, 0, 0);
02549 }
02550 
02551 SolidBrushCache::~SolidBrushCache()
02552 {
02553   // No need to delete the stock objects
02554   for (int i = 2; i < BRUSH_CACHE_SIZE; i++) {
02555     if (mCache[i].mBrush) {
02556       ::DeleteObject(mCache[i].mBrush);
02557     }
02558   }
02559 }
02560 
02561 HBRUSH
02562 SolidBrushCache::GetSolidBrush(HDC theHDC, COLORREF aColor)
02563 {
02564   int     i;
02565   HBRUSH  result = NULL;
02566   
02567   // See if it's already in the cache
02568   for (i = 0; (i < BRUSH_CACHE_SIZE) && mCache[i].mBrush; i++) {
02569     if (mCache[i].mBrush && (mCache[i].mBrushColor == aColor)) {
02570       // Found an existing brush
02571       result = mCache[i].mBrush;
02572       ::SelectObject(theHDC, result);
02573       break;
02574     }
02575   }
02576 
02577   if (!result) {
02578     // We didn't find it in the set of existing brushes, so create a
02579     // new brush
02580     result = (HBRUSH)::CreateSolidBrush(PALETTERGB_COLORREF(aColor));
02581 
02582     // Select the brush.  NOTE: we want to select the new brush before
02583     // deleting the old brush to prevent any win98 GDI leaks (bug 159298)
02584     ::SelectObject(theHDC, result);
02585 
02586     // If there's an empty slot in the cache, then just add it there
02587     if (i >= BRUSH_CACHE_SIZE) {
02588       // Nope. The cache is full so we need to replace the oldest entry
02589       // in the cache
02590       ::DeleteObject(mCache[mIndexOldest].mBrush);
02591       i = mIndexOldest;
02592       if (++mIndexOldest >= BRUSH_CACHE_SIZE) {
02593         mIndexOldest = 2;
02594       }
02595     }
02596 
02597     // Add the new entry
02598     mCache[i].mBrush = result;
02599     mCache[i].mBrushColor = aColor;
02600   }
02601 
02602   return result;
02603 }
02604 
02605 static SolidBrushCache  gSolidBrushCache;
02606 
02607 HBRUSH nsRenderingContextWin :: SetupSolidBrush(void)
02608 {
02609   if ((mCurrentColor != mCurrBrushColor) || (NULL == mCurrBrush))
02610   {
02611     HBRUSH tbrush = gSolidBrushCache.GetSolidBrush(mDC, mColor);
02612 
02613     mCurrBrush = tbrush;
02614     mCurrBrushColor = mCurrentColor;
02615   }
02616 
02617   return mCurrBrush;
02618 }
02619 
02620 void nsRenderingContextWin :: SetupFontAndColor(void)
02621 {
02622   if (mFontMetrics && (!mCurrFontWin || mCurrFontWin->mFont != mCurrFont)) {
02623     nsFontHandle  fontHandle;
02624     mFontMetrics->GetFontHandle(fontHandle);
02625     HFONT         tfont = (HFONT)fontHandle;
02626 
02627     ::SelectObject(mDC, tfont);
02628 
02629     mCurrFont = tfont;
02630     mCurrFontWin = ((nsFontMetricsWin*)mFontMetrics)->GetFontFor(mCurrFont);
02631 
02632     // nsFontMetricsWin vs. nsFontMetricsWinA
02633     // When making changes in the font code, set |useAFunctions = 1| in nsGfxFactoryWin
02634     // to verify that the changes didn't let the 'A' versions out of sync. 
02635     NS_ASSERTION(mCurrFontWin, "internal error");
02636   }
02637 
02638   if (mCurrentColor != mCurrTextColor)
02639   {
02640     ::SetTextColor(mDC, PALETTERGB_COLORREF(mColor));
02641     mCurrTextColor = mCurrentColor;
02642   }
02643 }
02644 
02645 HPEN nsRenderingContextWin :: SetupPen()
02646 {
02647   HPEN pen;
02648 
02649   switch(mCurrLineStyle)
02650   {
02651     case nsLineStyle_kSolid:
02652       pen = SetupSolidPen();
02653       break;
02654 
02655     case nsLineStyle_kDashed:
02656       pen = SetupDashedPen();
02657       break;
02658 
02659     case nsLineStyle_kDotted:
02660       pen = SetupDottedPen();
02661       break;
02662 
02663     case nsLineStyle_kNone:
02664       pen = NULL;
02665       break;
02666 
02667     default:
02668       pen = SetupSolidPen();
02669       break;
02670   }
02671 
02672   return pen;
02673 }
02674 
02675 
02676 HPEN nsRenderingContextWin :: SetupSolidPen(void)
02677 {
02678   if ((mCurrentColor != mCurrPenColor) || (NULL == mCurrPen) || (mCurrPen != mStates->mSolidPen))
02679   {
02680     HPEN  tpen;
02681      
02682     if (RGB(0, 0, 0) == mColor) {
02683       tpen = gStockBlackPen;
02684     } else if (RGB(255, 255, 255) == mColor) {
02685       tpen = gStockWhitePen;
02686     } else {
02687       tpen = ::CreatePen(PS_SOLID, 0, PALETTERGB_COLORREF(mColor));
02688     }
02689 
02690     ::SelectObject(mDC, tpen);
02691 
02692     if (mCurrPen && (mCurrPen != gStockBlackPen) && (mCurrPen != gStockWhitePen)) {
02693       VERIFY(::DeleteObject(mCurrPen));
02694     }
02695 
02696     mStates->mSolidPen = mCurrPen = tpen;
02697     mCurrPenColor = mCurrentColor;
02698   }
02699 
02700   return mCurrPen;
02701 }
02702 
02703 HPEN nsRenderingContextWin :: SetupDashedPen(void)
02704 {
02705   if ((mCurrentColor != mCurrPenColor) || (NULL == mCurrPen) || (mCurrPen != mStates->mDashedPen))
02706   {
02707     HPEN  tpen = ::CreatePen(PS_DASH, 0, PALETTERGB_COLORREF(mColor));
02708 
02709     ::SelectObject(mDC, tpen);
02710 
02711     if (NULL != mCurrPen)
02712       VERIFY(::DeleteObject(mCurrPen));
02713 
02714     mStates->mDashedPen = mCurrPen = tpen;
02715     mCurrPenColor = mCurrentColor;
02716   }
02717 
02718   return mCurrPen;
02719 }
02720 
02721 HPEN nsRenderingContextWin :: SetupDottedPen(void)
02722 {
02723   if ((mCurrentColor != mCurrPenColor) || (NULL == mCurrPen) || (mCurrPen != mStates->mDottedPen))
02724   {
02725     HPEN  tpen = ::CreatePen(
02726 #ifndef WINCE
02727                              PS_DOT, 
02728 #else
02729                              PS_DASH,
02730 #endif
02731                              0, PALETTERGB_COLORREF(mColor));
02732 
02733 
02734     ::SelectObject(mDC, tpen);
02735 
02736     if (NULL != mCurrPen)
02737       VERIFY(::DeleteObject(mCurrPen));
02738 
02739     mStates->mDottedPen = mCurrPen = tpen;
02740     mCurrPenColor = mCurrentColor;
02741   }
02742 
02743   return mCurrPen;
02744 }
02745 
02746 //========================================================
02747 
02748 NS_IMETHODIMP 
02749 nsRenderingContextWin::SetPenMode(nsPenMode aPenMode)
02750 {
02751 
02752   switch(aPenMode){
02753   case nsPenMode_kNone:
02754     ::SetROP2(mDC,R2_COPYPEN);
02755     mPenMode = nsPenMode_kNone;
02756     break;
02757   case nsPenMode_kInvert:
02758     ::SetROP2(mDC,R2_NOT);
02759     mPenMode = nsPenMode_kInvert;
02760     break;
02761   }
02762 
02763   return NS_OK;
02764 }
02765 
02766 //========================================================
02767 
02768 NS_IMETHODIMP 
02769 nsRenderingContextWin::GetPenMode(nsPenMode &aPenMode)
02770 {
02771   // can use the ::GetROP2(mDC); for debugging, see if windows is in the correct mode
02772   aPenMode = mPenMode;
02773 
02774   return NS_OK;
02775 }
02776 
02777 //========================================================
02778 
02779 void nsRenderingContextWin :: PushClipState(void)
02780 {
02781   if (!(mStates->mFlags & FLAG_CLIP_CHANGED))
02782   {
02783     GraphicsState *tstate = mStates->mNext;
02784 
02785     //we have never set a clip on this state before, so
02786     //remember the current clip state in the next state on the
02787     //stack. kind of wacky, but avoids selecting stuff in the DC
02788     //all the damned time.
02789 
02790     if (nsnull != tstate)
02791     {
02792       if (NULL == tstate->mClipRegion)
02793         tstate->mClipRegion = ::CreateRectRgn(0, 0, 0, 0);
02794 
02795       if (::GetClipRgn(mDC, tstate->mClipRegion) == 1)
02796         tstate->mFlags |= FLAG_CLIP_VALID;
02797       else
02798         tstate->mFlags &= ~FLAG_CLIP_VALID;
02799     }
02800   
02801     mStates->mFlags |= FLAG_CLIP_CHANGED;
02802   }
02803 }
02804 
02805 NS_IMETHODIMP nsRenderingContextWin :: CreateDrawingSurface(HDC aDC, nsIDrawingSurface* &aSurface)
02806 {
02807   nsDrawingSurfaceWin *surf = new nsDrawingSurfaceWin();
02808 
02809   if (nsnull != surf)
02810   {
02811     NS_ADDREF(surf);
02812     surf->Init(aDC);
02813   }
02814 
02815   aSurface = surf;
02816 
02817   return NS_OK;
02818 }
02819 
02830 // XXX: TODO find all calls under WIN95 that will fail if passed large coordinate values
02831 // and make calls to this method to fix them.
02832 
02833 void 
02834 nsRenderingContextWin::ConditionRect(nsRect& aSrcRect, RECT& aDestRect)
02835 {
02836   // There is no limit in NT class Windows versions (this includes W2K and XP)
02837   if (!gIsWIN95)
02838   {
02839     aDestRect.top = aSrcRect.y;
02840     aDestRect.bottom = aSrcRect.y + aSrcRect.height;
02841     aDestRect.left = aSrcRect.x;
02842     aDestRect.right = aSrcRect.x + aSrcRect.width;
02843     return;
02844   }
02845 
02846   // The following is for WIN95, WIN98 and WINME
02847   const nscoord kBottomRightLimit = 16384;
02848   const nscoord kTopLeftLimit = -8192;
02849 
02850   aDestRect.top = (aSrcRect.y < kTopLeftLimit)
02851                       ? kTopLeftLimit
02852                       : aSrcRect.y;
02853   aDestRect.bottom = ((aSrcRect.y + aSrcRect.height) > kBottomRightLimit)
02854                       ? kBottomRightLimit
02855                       : (aSrcRect.y + aSrcRect.height);
02856   aDestRect.left = (aSrcRect.x < kTopLeftLimit)
02857                       ? kTopLeftLimit
02858                       : aSrcRect.x;
02859   aDestRect.right = ((aSrcRect.x + aSrcRect.width) > kBottomRightLimit)
02860                       ? kBottomRightLimit
02861                       : (aSrcRect.x + aSrcRect.width);
02862 }
02863 
02864 
02865 NS_IMETHODIMP 
02866 nsRenderingContextWin::GetBackbuffer(const nsRect &aRequestedSize, const nsRect &aMaxSize, PRBool aForBlending, nsIDrawingSurface* &aBackbuffer)
02867 {
02868   // Do not cache the backbuffer. On WIN32 it is faster to get allocate
02869   // the backbuffer as needed. @see bug 95952
02870   return AllocateBackbuffer(aRequestedSize, aMaxSize, aBackbuffer, PR_FALSE, aForBlending ? NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS : 0);
02871 }
02872  
02873 NS_IMETHODIMP 
02874 nsRenderingContextWin::ReleaseBackbuffer(void) {
02875   // Destroy the backbuffer. Do not cache it. On WIN32 it is faster to get allocate
02876   // the backbuffer as needed. @see bug 95952
02877   return DestroyCachedBackbuffer();
02878 }
02879 
02885 NS_IMETHODIMP
02886 nsRenderingContextWin::SetRightToLeftText(PRBool aIsRTL)
02887 {
02888 #ifndef WINCE
02889   // Only call SetTextAlign if the new value is different from the
02890   // current value
02891   if (aIsRTL != mRightToLeftText) {
02892     UINT flags = ::GetTextAlign(mDC);
02893     if (aIsRTL) {
02894       flags |= TA_RTLREADING;
02895     }
02896     else {
02897       flags &= (~TA_RTLREADING);
02898     }
02899     ::SetTextAlign(mDC, flags);
02900   }
02901 
02902   mRightToLeftText = aIsRTL;
02903 #endif
02904   return NS_OK;
02905 }
02906 
02907 NS_IMETHODIMP
02908 nsRenderingContextWin::GetRightToLeftText(PRBool* aIsRTL)
02909 {
02910 #ifndef WINCE
02911   *aIsRTL = mRightToLeftText;
02912 #else
02913   *aIsRTL = PR_FALSE;
02914 #endif
02915   return NS_OK;
02916 }
02917 
02922 void
02923 nsRenderingContextWin::InitBidiInfo()
02924 {
02925 #ifndef WINCE
02926   if (NOT_SETUP == gBidiInfo) {
02927     gBidiInfo = DONT_INIT;
02928 
02929     const PRUnichar araAin  = 0x0639;
02930     const PRUnichar one     = 0x0031;
02931 
02932     int distanceArray[2];
02933     PRUnichar glyphArray[2];
02934     PRUnichar outStr[] = {0, 0};
02935 
02936     GCP_RESULTSW gcpResult;
02937     gcpResult.lStructSize = sizeof(GCP_RESULTS);
02938     gcpResult.lpOutString = outStr;     // Output string
02939     gcpResult.lpOrder = nsnull;         // Ordering indices
02940     gcpResult.lpDx = distanceArray;     // Distances between character cells
02941     gcpResult.lpCaretPos = nsnull;      // Caret positions
02942     gcpResult.lpClass = nsnull;         // Character classifications
02943     gcpResult.lpGlyphs = glyphArray;    // Character glyphs
02944     gcpResult.nGlyphs = 2;              // Array size
02945 
02946     PRUnichar inStr[] = {araAin, one};
02947 
02948     if (::GetCharacterPlacementW(mDC, inStr, 2, 0, &gcpResult, GCP_REORDER) 
02949         && (inStr[0] == outStr[1]) ) {
02950       gBidiInfo = GCP_REORDER | GCP_GLYPHSHAPE;
02951 #ifdef NS_DEBUG
02952       printf("System has shaping\n");
02953 #endif
02954     }
02955     else {
02956       const PRUnichar hebAlef = 0x05D0;
02957       inStr[0] = hebAlef;
02958       inStr[1] = one;
02959       if (::GetCharacterPlacementW(mDC, inStr, 2, 0, &gcpResult, GCP_REORDER) 
02960           && (inStr[0] == outStr[1]) ) {
02961         gBidiInfo = GCP_REORDER;
02962 #ifdef NS_DEBUG
02963         printf("System has Bidi\n");
02964 #endif
02965       }
02966     }
02967   }
02968 #endif //WINCE
02969 }
02970 
02971 
02972