Back to index

lightning-sunbird  0.9+nobinonly
nsRenderingContextImpl.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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsCOMPtr.h"
00039 #include "nsRenderingContextImpl.h"
00040 #include "nsIDeviceContext.h"
00041 #include "nsIImage.h"
00042 #include "nsTransform2D.h"
00043 #include "nsIRegion.h"
00044 #include "nsIFontMetrics.h"
00045 #include "nsUnicharUtils.h"
00046 #include <stdlib.h>
00047 
00048 
00049 nsIDrawingSurface* nsRenderingContextImpl::gBackbuffer = nsnull;
00050 nsRect nsRenderingContextImpl::gBackbufferBounds = nsRect(0, 0, 0, 0);
00051 nsSize nsRenderingContextImpl::gLargestRequestedSize = nsSize(0, 0);
00052 
00053 
00058 nsRenderingContextImpl :: nsRenderingContextImpl()
00059 : mTranMatrix(nsnull)
00060 , mAct(0)
00061 , mActive(nsnull)
00062 , mPenMode(nsPenMode_kNone)
00063 {
00064 }
00065 
00070 nsRenderingContextImpl :: ~nsRenderingContextImpl()
00071 {
00072 
00073 
00074 }
00075 
00076 
00077 NS_IMETHODIMP nsRenderingContextImpl::GetBackbuffer(const nsRect &aRequestedSize, const nsRect &aMaxSize, PRBool aForBlending, nsIDrawingSurface* &aBackbuffer)
00078 {
00079   // Default implementation assumes the backbuffer will be cached.
00080   // If the platform implementation does not require the backbuffer to
00081   // be cached override this method and make the following call instead:
00082   // AllocateBackbuffer(aRequestedSize, aMaxSize, aBackbuffer, PR_FALSE);
00083   return AllocateBackbuffer(aRequestedSize, aMaxSize, aBackbuffer, PR_TRUE, 0);
00084 }
00085 
00086 nsresult nsRenderingContextImpl::AllocateBackbuffer(const nsRect &aRequestedSize, const nsRect &aMaxSize, nsIDrawingSurface* &aBackbuffer, PRBool aCacheBackbuffer, PRUint32 aSurfFlags)
00087 {
00088   nsRect newBounds;
00089   nsresult rv = NS_OK;
00090 
00091    if (! aCacheBackbuffer) {
00092     newBounds = aRequestedSize;
00093   } else {
00094     GetDrawingSurfaceSize(aMaxSize, aRequestedSize, newBounds);
00095   }
00096 
00097   if ((nsnull == gBackbuffer)
00098       || (gBackbufferBounds.width != newBounds.width)
00099       || (gBackbufferBounds.height != newBounds.height))
00100     {
00101       if (gBackbuffer) {
00102         //destroy existing DS
00103         DestroyDrawingSurface(gBackbuffer);
00104         gBackbuffer = nsnull;
00105       }
00106 
00107       rv = CreateDrawingSurface(newBounds, aSurfFlags, gBackbuffer);
00108       //   printf("Allocating a new drawing surface %d %d\n", newBounds.width, newBounds.height);
00109       if (NS_SUCCEEDED(rv)) {
00110         gBackbufferBounds = newBounds;
00111         SelectOffScreenDrawingSurface(gBackbuffer);
00112       } else {
00113         gBackbufferBounds.SetRect(0,0,0,0);
00114         gBackbuffer = nsnull;
00115       }
00116     } else {
00117       SelectOffScreenDrawingSurface(gBackbuffer);
00118 
00119       float p2t;
00120       nsCOMPtr<nsIDeviceContext>  dx;
00121       GetDeviceContext(*getter_AddRefs(dx));
00122       p2t = dx->DevUnitsToAppUnits();
00123       nsRect bounds = aRequestedSize;
00124       bounds *= p2t;
00125 
00126       SetClipRect(bounds, nsClipCombine_kReplace);
00127     }
00128 
00129   aBackbuffer = gBackbuffer;
00130   return rv;
00131 }
00132 
00133 NS_IMETHODIMP nsRenderingContextImpl::ReleaseBackbuffer(void)
00134 {
00135   // If the platform does not require the backbuffer to be cached
00136   // override this method and call DestroyCachedBackbuffer
00137   return NS_OK;
00138 }
00139 
00140 NS_IMETHODIMP nsRenderingContextImpl::DestroyCachedBackbuffer(void)
00141 {
00142   if (gBackbuffer) {
00143     DestroyDrawingSurface(gBackbuffer);
00144     gBackbuffer = nsnull;
00145   }
00146   return NS_OK;
00147 }
00148 
00149 NS_IMETHODIMP nsRenderingContextImpl::UseBackbuffer(PRBool* aUseBackbuffer)
00150 {
00151   *aUseBackbuffer = PR_TRUE;
00152   return NS_OK;
00153 }
00154 
00155 NS_IMETHODIMP nsRenderingContextImpl::PushTranslation(PushedTranslation* aState)
00156 {
00157   // The transform components are saved and restored instead 
00158   // of using PushState and PopState because they are too slow
00159   // because they also save and restore the clip state.
00160   // Note: Setting a negative translation to restore the 
00161   // state does not work because the floating point errors can accumulate
00162   // causing the display of some frames to be off by one pixel. 
00163   // This happens frequently when running in 120DPI mode where frames are
00164   // often positioned at 1/2 pixel locations and small floating point errors
00165   // will cause the frames to vary their pixel x location during scrolling
00166   // operations causes a single scan line of pixels to be shifted left relative
00167   // to the other scan lines for the same text. 
00168   
00169   // Save the transformation matrix's translation components.
00170   nsTransform2D *theTransform; 
00171   GetCurrentTransform(theTransform);
00172   NS_ASSERTION(theTransform != nsnull, "The rendering context transform is null");
00173   theTransform->GetTranslation(&aState->mSavedX, &aState->mSavedY);
00174   
00175   return NS_OK;
00176 }
00177 
00178 NS_IMETHODIMP nsRenderingContextImpl::PopTranslation(PushedTranslation* aState)
00179 {
00180   nsTransform2D *theTransform; 
00181   GetCurrentTransform(theTransform);
00182   NS_ASSERTION(theTransform != nsnull, "The rendering context transform is null");
00183   theTransform->SetTranslation(aState->mSavedX, aState->mSavedY);
00184 
00185   return NS_OK;
00186 }
00187 
00188 PRBool nsRenderingContextImpl::RectFitsInside(const nsRect& aRect, PRInt32 aWidth, PRInt32 aHeight) const
00189 {
00190   if (aRect.width > aWidth)
00191     return (PR_FALSE);
00192 
00193   if (aRect.height > aHeight)
00194     return (PR_FALSE);
00195 
00196   return PR_TRUE;
00197 }
00198 
00199 PRBool nsRenderingContextImpl::BothRectsFitInside(const nsRect& aRect1, const nsRect& aRect2, PRInt32 aWidth, PRInt32 aHeight, nsRect& aNewSize) const
00200 {
00201   if (PR_FALSE == RectFitsInside(aRect1, aWidth, aHeight)) {
00202     return PR_FALSE;
00203   }
00204 
00205   if (PR_FALSE == RectFitsInside(aRect2, aWidth, aHeight)) {
00206     return PR_FALSE;
00207   }
00208 
00209   aNewSize.width = aWidth;
00210   aNewSize.height = aHeight;
00211 
00212   return PR_TRUE;
00213 }
00214 
00215 void nsRenderingContextImpl::GetDrawingSurfaceSize(const nsRect& aMaxBackbufferSize, const nsRect& aRequestedSize, nsRect& aNewSize) 
00216 { 
00217   CalculateDiscreteSurfaceSize(aMaxBackbufferSize, aRequestedSize, aNewSize);
00218   aNewSize.MoveTo(aRequestedSize.x, aRequestedSize.y);
00219 }
00220 
00221 void nsRenderingContextImpl::CalculateDiscreteSurfaceSize(const nsRect& aMaxBackbufferSize, const nsRect& aRequestedSize, nsRect& aSurfaceSize) 
00222 {
00223   // Get the height and width of the screen
00224   PRInt32 height;
00225   PRInt32 width;
00226 
00227   nsCOMPtr<nsIDeviceContext>  dx;
00228   GetDeviceContext(*getter_AddRefs(dx));
00229   dx->GetDeviceSurfaceDimensions(width, height);
00230 
00231   float devUnits;
00232   devUnits = dx->DevUnitsToAppUnits();
00233   PRInt32 screenHeight = NSToIntRound(float( height) / devUnits );
00234   PRInt32 screenWidth = NSToIntRound(float( width) / devUnits );
00235 
00236   // These tests must go from smallest rectangle to largest rectangle.
00237 
00238   // 1/8 screen
00239   if (BothRectsFitInside(aRequestedSize, aMaxBackbufferSize, screenWidth / 8, screenHeight / 8, aSurfaceSize)) {
00240     return;
00241   }
00242 
00243   // 1/4 screen
00244   if (BothRectsFitInside(aRequestedSize, aMaxBackbufferSize, screenWidth / 4, screenHeight / 4, aSurfaceSize)) {
00245     return;
00246   }
00247 
00248   // 1/2 screen
00249   if (BothRectsFitInside(aRequestedSize, aMaxBackbufferSize, screenWidth / 2, screenHeight / 2, aSurfaceSize)) {
00250     return;
00251   }
00252 
00253   // 3/4 screen
00254   if (BothRectsFitInside(aRequestedSize, aMaxBackbufferSize, (screenWidth * 3) / 4, (screenHeight * 3) / 4, aSurfaceSize)) {
00255     return;
00256   }
00257 
00258   // 3/4 screen width full screen height
00259   if (BothRectsFitInside(aRequestedSize, aMaxBackbufferSize, (screenWidth * 3) / 4, screenHeight, aSurfaceSize)) {
00260     return;
00261   }
00262 
00263   // Full screen
00264   if (BothRectsFitInside(aRequestedSize, aMaxBackbufferSize, screenWidth, screenHeight, aSurfaceSize)) {
00265     return;
00266   }
00267 
00268   // Bigger than Full Screen use the largest request every made.
00269   if (BothRectsFitInside(aRequestedSize, aMaxBackbufferSize, gLargestRequestedSize.width, gLargestRequestedSize.height, aSurfaceSize)) {
00270     return;
00271   } else {
00272     gLargestRequestedSize.width = PR_MAX(aRequestedSize.width, aMaxBackbufferSize.width);
00273     gLargestRequestedSize.height = PR_MAX(aRequestedSize.height, aMaxBackbufferSize.height);
00274     aSurfaceSize.width = gLargestRequestedSize.width;
00275     aSurfaceSize.height = gLargestRequestedSize.height;
00276     //   printf("Expanding the largested requested size to %d %d\n", gLargestRequestedSize.width, gLargestRequestedSize.height);
00277   }
00278 }
00279 
00280 
00281 
00286 NS_IMETHODIMP
00287 nsRenderingContextImpl::SetRightToLeftText(PRBool aIsRTL)
00288 {
00289   return NS_OK;
00290 }
00291 
00292 NS_IMETHODIMP
00293 nsRenderingContextImpl::GetRightToLeftText(PRBool* aIsRTL)
00294 {
00295   *aIsRTL = PR_FALSE;
00296   return NS_OK;
00297 }
00298 
00299 #include "imgIContainer.h"
00300 #include "gfxIImageFrame.h"
00301 #include "nsIInterfaceRequestor.h"
00302 #include "nsIInterfaceRequestorUtils.h"
00303 
00304 NS_IMETHODIMP nsRenderingContextImpl::DrawImage(imgIContainer *aImage, const nsRect & aSrcRect, const nsRect & aDestRect)
00305 {
00306   nsRect dr = aDestRect;
00307   mTranMatrix->TransformCoord(&dr.x, &dr.y, &dr.width, &dr.height);
00308 
00309   nsRect sr = aSrcRect;
00310   mTranMatrix->TransformCoord(&sr.x, &sr.y, &sr.width, &sr.height);
00311   
00312   if (sr.IsEmpty() || dr.IsEmpty())
00313     return NS_OK;
00314 
00315   sr.x = aSrcRect.x;
00316   sr.y = aSrcRect.y;
00317   mTranMatrix->TransformNoXLateCoord(&sr.x, &sr.y);
00318 
00319   nsCOMPtr<gfxIImageFrame> iframe;
00320   aImage->GetCurrentFrame(getter_AddRefs(iframe));
00321   if (!iframe) return NS_ERROR_FAILURE;
00322 
00323   nsCOMPtr<nsIImage> img(do_GetInterface(iframe));
00324   if (!img) return NS_ERROR_FAILURE;
00325 
00326   nsIDrawingSurface *surface = nsnull;
00327   GetDrawingSurface(&surface);
00328   if (!surface) return NS_ERROR_FAILURE;
00329 
00330   // For Bug 87819
00331   // iframe may want image to start at different position, so adjust
00332   nsRect iframeRect;
00333   iframe->GetRect(iframeRect);
00334   
00335   if (iframeRect.x > 0) {
00336     // Adjust for the iframe offset before we do scaling.
00337     sr.x -= iframeRect.x;
00338 
00339     nscoord scaled_x = sr.x;
00340     if (dr.width != sr.width) {
00341       PRFloat64 scale_ratio = PRFloat64(dr.width) / PRFloat64(sr.width);
00342       scaled_x = NSToCoordRound(scaled_x * scale_ratio);
00343     }
00344     if (sr.x < 0) {
00345       dr.x -= scaled_x;
00346       sr.width += sr.x;
00347       dr.width += scaled_x;
00348       if (sr.width <= 0 || dr.width <= 0)
00349         return NS_OK;
00350       sr.x = 0;
00351     } else if (sr.x > iframeRect.width) {
00352       return NS_OK;
00353     }
00354   }
00355 
00356   if (iframeRect.y > 0) {
00357     // Adjust for the iframe offset before we do scaling.
00358     sr.y -= iframeRect.y;
00359 
00360     nscoord scaled_y = sr.y;
00361     if (dr.height != sr.height) {
00362       PRFloat64 scale_ratio = PRFloat64(dr.height) / PRFloat64(sr.height);
00363       scaled_y = NSToCoordRound(scaled_y * scale_ratio);
00364     }
00365     if (sr.y < 0) {
00366       dr.y -= scaled_y;
00367       sr.height += sr.y;
00368       dr.height += scaled_y;
00369       if (sr.height <= 0 || dr.height <= 0)
00370         return NS_OK;
00371       sr.y = 0;
00372     } else if (sr.y > iframeRect.height) {
00373       return NS_OK;
00374     }
00375   }
00376 
00377   // Multiple paint rects may have been coalesced into a bounding box, so
00378   // ensure that this rect is actually within the clip region before we draw.
00379   nsCOMPtr<nsIRegion> clipRegion;
00380   GetClipRegion(getter_AddRefs(clipRegion));
00381   if (clipRegion && !clipRegion->ContainsRect(dr.x, dr.y, dr.width, dr.height))
00382     return NS_OK;
00383 
00384   return img->Draw(*this, surface, sr.x, sr.y, sr.width, sr.height,
00385                    dr.x, dr.y, dr.width, dr.height);
00386 }
00387 
00388 /* [noscript] void drawTile (in imgIContainer aImage, in nscoord aXImageStart, in nscoord aYImageStart, [const] in nsRect aTargetRect); */
00389 NS_IMETHODIMP
00390 nsRenderingContextImpl::DrawTile(imgIContainer *aImage,
00391                                  nscoord aXImageStart, nscoord aYImageStart,
00392                                  const nsRect * aTargetRect)
00393 {
00394   nsRect dr(*aTargetRect);
00395   mTranMatrix->TransformCoord(&dr.x, &dr.y, &dr.width, &dr.height);
00396   mTranMatrix->TransformCoord(&aXImageStart, &aYImageStart);
00397 
00398   // may have become empty due to transform shinking small number to 0
00399   if (dr.IsEmpty())
00400     return NS_OK;
00401 
00402   nscoord width, height;
00403   aImage->GetWidth(&width);
00404   aImage->GetHeight(&height);
00405 
00406   if (width == 0 || height == 0)
00407     return NS_OK;
00408 
00409   nscoord xOffset = (dr.x - aXImageStart) % width;
00410   nscoord yOffset = (dr.y - aYImageStart) % height;
00411 
00412   nsCOMPtr<gfxIImageFrame> iframe;
00413   aImage->GetCurrentFrame(getter_AddRefs(iframe));
00414   if (!iframe) return NS_ERROR_FAILURE;
00415 
00416   nsCOMPtr<nsIImage> img(do_GetInterface(iframe));
00417   if (!img) return NS_ERROR_FAILURE;
00418 
00419   nsIDrawingSurface *surface = nsnull;
00420   GetDrawingSurface(&surface);
00421   if (!surface) return NS_ERROR_FAILURE;
00422 
00423   /* bug 113561 - frame can be smaller than container */
00424   nsRect iframeRect;
00425   iframe->GetRect(iframeRect);
00426   PRInt32 padx = width - iframeRect.width;
00427   PRInt32 pady = height - iframeRect.height;
00428 
00429   return img->DrawTile(*this, surface,
00430                        xOffset - iframeRect.x, yOffset - iframeRect.y,
00431                        padx, pady,
00432                        dr);
00433 }
00434 
00435 NS_IMETHODIMP
00436 nsRenderingContextImpl::FlushRect(const nsRect& aRect)
00437 {
00438   return NS_OK;
00439 }
00440 
00441 NS_IMETHODIMP
00442 nsRenderingContextImpl::FlushRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
00443 {
00444     return NS_OK;
00445 }
00446 
00447 NS_IMETHODIMP
00448 nsRenderingContextImpl::GetClusterInfo(const PRUnichar *aText,
00449                                        PRUint32 aLength,
00450                                        PRUint8 *aClusterStarts)
00451 {
00452   return NS_ERROR_NOT_IMPLEMENTED;
00453 }
00454 
00455 PRInt32
00456 nsRenderingContextImpl::GetPosition(const PRUnichar *aText,
00457                                     PRUint32 aLength,
00458                                     nsPoint aPt)
00459 {
00460   return -1;
00461 }
00462 
00463 NS_IMETHODIMP
00464 nsRenderingContextImpl::GetRangeWidth(const PRUnichar *aText,
00465                                       PRUint32 aLength,
00466                                       PRUint32 aStart,
00467                                       PRUint32 aEnd,
00468                                       PRUint32 &aWidth)
00469 {
00470   return NS_ERROR_NOT_IMPLEMENTED;
00471 }
00472 
00473 NS_IMETHODIMP
00474 nsRenderingContextImpl::GetRangeWidth(const char *aText,
00475                                       PRUint32 aLength,
00476                                       PRUint32 aStart,
00477                                       PRUint32 aEnd,
00478                                       PRUint32 &aWidth)
00479 {
00480   return NS_ERROR_NOT_IMPLEMENTED;
00481 }
00482 
00483 // Hard limit substring lengths to 8000 characters ... this lets us statically
00484 // size the cluster buffer array in FindSafeLength
00485 #define MAX_GFX_TEXT_BUF_SIZE 8000
00486 static PRInt32 GetMaxChunkLength(nsRenderingContextImpl* aContext)
00487 {
00488   PRInt32 len = aContext->GetMaxStringLength();
00489   return PR_MIN(len, MAX_GFX_TEXT_BUF_SIZE);
00490 }
00491 
00492 static PRInt32 FindSafeLength(nsRenderingContextImpl* aContext,
00493                               const PRUnichar *aString, PRUint32 aLength,
00494                               PRUint32 aMaxChunkLength)
00495 {
00496   if (aLength <= aMaxChunkLength)
00497     return aLength;
00498   
00499   PRUint8 buffer[MAX_GFX_TEXT_BUF_SIZE + 1];
00500   // Fill in the cluster hint information, if it's available.
00501   PRUint32 clusterHint;
00502   aContext->GetHints(clusterHint);
00503   clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS;
00504 
00505   PRInt32 len = aMaxChunkLength;
00506 
00507   if (clusterHint) {
00508     nsresult rv =
00509       aContext->GetClusterInfo(aString, aMaxChunkLength + 1, buffer);
00510     if (NS_FAILED(rv))
00511       return len;
00512   }
00513 
00514   // Ensure that we don't break inside a cluster or inside a surrogate pair
00515   while (len > 0 &&
00516          (IS_LOW_SURROGATE(aString[len]) || (clusterHint && !buffer[len]))) {
00517     len--;
00518   }
00519   if (len == 0) {
00520     // We don't want our caller to go into an infinite loop, so don't return
00521     // zero. It's hard to imagine how we could actually get here unless there
00522     // are languages that allow clusters of arbitrary size. If there are and
00523     // someone feeds us a 500+ character cluster, too bad.
00524     return aMaxChunkLength;
00525   }
00526   return len;
00527 }
00528 
00529 static PRInt32 FindSafeLength(nsRenderingContextImpl* aContext,
00530                               const char *aString, PRUint32 aLength,
00531                               PRUint32 aMaxChunkLength)
00532 {
00533   // Since it's ASCII, we don't need to worry about clusters or RTL
00534   return PR_MIN(aLength, aMaxChunkLength);
00535 }
00536 
00537 NS_IMETHODIMP
00538 nsRenderingContextImpl::GetWidth(const nsString& aString, nscoord &aWidth,
00539                                  PRInt32 *aFontID)
00540 {
00541   return GetWidth(aString.get(), aString.Length(), aWidth, aFontID);
00542 }
00543 
00544 NS_IMETHODIMP
00545 nsRenderingContextImpl::GetWidth(const char* aString, nscoord& aWidth)
00546 {
00547   return GetWidth(aString, strlen(aString), aWidth);
00548 }
00549 
00550 NS_IMETHODIMP
00551 nsRenderingContextImpl::DrawString(const nsString& aString, nscoord aX, nscoord aY,
00552                                    PRInt32 aFontID, const nscoord* aSpacing)
00553 {
00554   return DrawString(aString.get(), aString.Length(), aX, aY, aFontID, aSpacing);
00555 }
00556 
00557 NS_IMETHODIMP
00558 nsRenderingContextImpl::GetWidth(const char* aString, PRUint32 aLength,
00559                                  nscoord& aWidth)
00560 {
00561   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00562   aWidth = 0;
00563   while (aLength > 0) {
00564     PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength);
00565     nscoord width;
00566     nsresult rv = GetWidthInternal(aString, len, width);
00567     if (NS_FAILED(rv))
00568       return rv;
00569     aWidth += width;
00570     aLength -= len;
00571     aString += len;
00572   }
00573   return NS_OK;
00574 }
00575 
00576 NS_IMETHODIMP
00577 nsRenderingContextImpl::GetWidth(const PRUnichar *aString, PRUint32 aLength,
00578                                  nscoord &aWidth, PRInt32 *aFontID)
00579 {
00580   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00581   aWidth = 0;
00582   
00583   if (aFontID) {
00584     *aFontID = 0;
00585   }
00586   
00587   while (aLength > 0) {
00588     PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength);
00589     nscoord width;
00590     nsresult rv = GetWidthInternal(aString, len, width);
00591     if (NS_FAILED(rv))
00592       return rv;
00593     aWidth += width;
00594     aLength -= len;
00595     aString += len;
00596   }
00597   return NS_OK;
00598 }  
00599 
00600 NS_IMETHODIMP
00601 nsRenderingContextImpl::GetTextDimensions(const char* aString, PRUint32 aLength,
00602                                           nsTextDimensions& aDimensions)
00603 {
00604   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00605   if (aLength <= maxChunkLength)
00606     return GetTextDimensionsInternal(aString, aLength, aDimensions);
00607  
00608   PRBool firstIteration = PR_TRUE;
00609   while (aLength > 0) {
00610     PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength);
00611     nsTextDimensions dimensions;
00612     nsresult rv = GetTextDimensionsInternal(aString, len, dimensions);
00613     if (NS_FAILED(rv))
00614       return rv;
00615     if (firstIteration) {
00616       // Instead of combining with a Clear()ed nsTextDimensions, we assign
00617       // directly in the first iteration. This ensures that negative ascent/
00618       // descent can be returned.
00619       aDimensions = dimensions;
00620     } else {
00621       aDimensions.Combine(dimensions);
00622     }
00623     aLength -= len;
00624     aString += len;
00625     firstIteration = PR_FALSE;
00626   }
00627   return NS_OK;
00628 }
00629 
00630 NS_IMETHODIMP
00631 nsRenderingContextImpl::GetTextDimensions(const PRUnichar* aString, PRUint32 aLength,
00632                                           nsTextDimensions& aDimensions, PRInt32* aFontID)
00633 {
00634   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00635   if (aLength <= maxChunkLength)
00636     return GetTextDimensionsInternal(aString, aLength, aDimensions);
00637     
00638   if (aFontID) {
00639     *aFontID = nsnull;
00640   }
00641  
00642   PRBool firstIteration = PR_TRUE;
00643   while (aLength > 0) {
00644     PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength);
00645     nsTextDimensions dimensions;
00646     nsresult rv = GetTextDimensionsInternal(aString, len, dimensions);
00647     if (NS_FAILED(rv))
00648       return rv;
00649     if (firstIteration) {
00650       // Instead of combining with a Clear()ed nsTextDimensions, we assign
00651       // directly in the first iteration. This ensures that negative ascent/
00652       // descent can be returned.
00653       aDimensions = dimensions;
00654     } else {
00655       aDimensions.Combine(dimensions);
00656     }
00657     aLength -= len;
00658     aString += len;
00659     firstIteration = PR_FALSE;
00660   }
00661   return NS_OK;  
00662 }
00663 
00664 #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)
00665 NS_IMETHODIMP
00666 nsRenderingContextImpl::GetTextDimensions(const char*       aString,
00667                                           PRInt32           aLength,
00668                                           PRInt32           aAvailWidth,
00669                                           PRInt32*          aBreaks,
00670                                           PRInt32           aNumBreaks,
00671                                           nsTextDimensions& aDimensions,
00672                                           PRInt32&          aNumCharsFit,
00673                                           nsTextDimensions& aLastWordDimensions,
00674                                           PRInt32*          aFontID)
00675 {
00676   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00677   if (aLength <= PRInt32(maxChunkLength))
00678     return GetTextDimensionsInternal(aString, aLength, aAvailWidth, aBreaks, aNumBreaks,
00679                                      aDimensions, aNumCharsFit, aLastWordDimensions, aFontID);
00680 
00681   if (aFontID) {
00682     *aFontID = 0;
00683   }
00684   
00685   // Do a naive implementation based on 3-arg GetTextDimensions
00686   PRInt32 x = 0;
00687   PRInt32 wordCount;
00688   for (wordCount = 0; wordCount < aNumBreaks; ++wordCount) {
00689     PRInt32 lastBreak = wordCount > 0 ? aBreaks[wordCount - 1] : 0;
00690     nsTextDimensions dimensions;
00691     
00692     NS_ASSERTION(aBreaks[wordCount] > lastBreak, "Breaks must be monotonically increasing");
00693     NS_ASSERTION(aBreaks[wordCount] <= aLength, "Breaks can't exceed string length");
00694    
00695      // Call safe method
00696 
00697     nsresult rv =
00698       GetTextDimensions(aString + lastBreak, aBreaks[wordCount] - lastBreak,
00699                         dimensions);
00700     if (NS_FAILED(rv))
00701       return rv;
00702     x += dimensions.width;
00703     // The first word always "fits"
00704     if (x > aAvailWidth && wordCount > 0)
00705       break;
00706     // aDimensions ascent/descent should exclude the last word (unless there
00707     // is only one word) so we let it run one word behind
00708     if (wordCount == 0) {
00709       aDimensions = dimensions;
00710     } else {
00711       aDimensions.Combine(aLastWordDimensions);
00712     }
00713     aNumCharsFit = aBreaks[wordCount];
00714     aLastWordDimensions = dimensions;
00715   }
00716   // aDimensions width should include all the text
00717   aDimensions.width = x;
00718   return NS_OK;
00719 }
00720 
00721 NS_IMETHODIMP
00722 nsRenderingContextImpl::GetTextDimensions(const PRUnichar*  aString,
00723                                           PRInt32           aLength,
00724                                           PRInt32           aAvailWidth,
00725                                           PRInt32*          aBreaks,
00726                                           PRInt32           aNumBreaks,
00727                                           nsTextDimensions& aDimensions,
00728                                           PRInt32&          aNumCharsFit,
00729                                           nsTextDimensions& aLastWordDimensions,
00730                                           PRInt32*          aFontID)
00731 {
00732   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00733   if (aLength <= PRInt32(maxChunkLength))
00734     return GetTextDimensionsInternal(aString, aLength, aAvailWidth, aBreaks, aNumBreaks,
00735                                      aDimensions, aNumCharsFit, aLastWordDimensions, aFontID);
00736 
00737   if (aFontID) {
00738     *aFontID = 0;
00739   }
00740 
00741   // Do a naive implementation based on 3-arg GetTextDimensions
00742   PRInt32 x = 0;
00743   PRInt32 wordCount;
00744   for (wordCount = 0; wordCount < aNumBreaks; ++wordCount) {
00745     PRInt32 lastBreak = wordCount > 0 ? aBreaks[wordCount - 1] : 0;
00746     
00747     NS_ASSERTION(aBreaks[wordCount] > lastBreak, "Breaks must be monotonically increasing");
00748     NS_ASSERTION(aBreaks[wordCount] <= aLength, "Breaks can't exceed string length");
00749     
00750     nsTextDimensions dimensions;
00751     // Call safe method
00752     nsresult rv =
00753       GetTextDimensions(aString + lastBreak, aBreaks[wordCount] - lastBreak,
00754                         dimensions);
00755     if (NS_FAILED(rv))
00756       return rv;
00757     x += dimensions.width;
00758     // The first word always "fits"
00759     if (x > aAvailWidth && wordCount > 0)
00760       break;
00761     // aDimensions ascent/descent should exclude the last word (unless there
00762     // is only one word) so we let it run one word behind
00763     if (wordCount == 0) {
00764       aDimensions = dimensions;
00765     } else {
00766       aDimensions.Combine(aLastWordDimensions);
00767     }
00768     aNumCharsFit = aBreaks[wordCount];
00769     aLastWordDimensions = dimensions;
00770   }
00771   // aDimensions width should include all the text
00772   aDimensions.width = x;
00773   return NS_OK;
00774 }
00775 #endif
00776 
00777 #ifdef MOZ_MATHML
00778 NS_IMETHODIMP
00779 nsRenderingContextImpl::GetBoundingMetrics(const char*        aString,
00780                                            PRUint32           aLength,
00781                                            nsBoundingMetrics& aBoundingMetrics)
00782 {
00783   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00784   if (aLength <= maxChunkLength)
00785     return GetBoundingMetricsInternal(aString, aLength, aBoundingMetrics);
00786 
00787   PRBool firstIteration = PR_TRUE;
00788   while (aLength > 0) {
00789     PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength);
00790     nsBoundingMetrics metrics;
00791     nsresult rv = GetBoundingMetricsInternal(aString, len, metrics);
00792     if (NS_FAILED(rv))
00793       return rv;
00794     if (firstIteration) {
00795       // Instead of combining with a Clear()ed nsBoundingMetrics, we assign
00796       // directly in the first iteration. This ensures that negative ascent/
00797       // descent can be returned and the left bearing is properly initialized.
00798       aBoundingMetrics = metrics;
00799     } else {
00800       aBoundingMetrics += metrics;
00801     }
00802     aLength -= len;
00803     aString += len;
00804     firstIteration = PR_FALSE;
00805   }  
00806   return NS_OK;
00807 }
00808 
00809 NS_IMETHODIMP
00810 nsRenderingContextImpl::GetBoundingMetrics(const PRUnichar*   aString,
00811                                            PRUint32           aLength,
00812                                            nsBoundingMetrics& aBoundingMetrics,
00813                                            PRInt32*           aFontID)
00814 {
00815   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00816   if (aLength <= maxChunkLength)
00817     return GetBoundingMetricsInternal(aString, aLength, aBoundingMetrics, aFontID);
00818 
00819   if (aFontID) {
00820     *aFontID = 0;
00821   }
00822 
00823   PRBool firstIteration = PR_TRUE;
00824   while (aLength > 0) {
00825     PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength);
00826     nsBoundingMetrics metrics;
00827     nsresult rv = GetBoundingMetricsInternal(aString, len, metrics);
00828     if (NS_FAILED(rv))
00829       return rv;
00830     if (firstIteration) {
00831       // Instead of combining with a Clear()ed nsBoundingMetrics, we assign
00832       // directly in the first iteration. This ensures that negative ascent/
00833       // descent can be returned and the left bearing is properly initialized.
00834       aBoundingMetrics = metrics;
00835     } else {
00836       aBoundingMetrics += metrics;
00837     }
00838     aLength -= len;
00839     aString += len;
00840     firstIteration = PR_FALSE;
00841   }  
00842   return NS_OK;
00843 }
00844 #endif
00845 
00846 NS_IMETHODIMP
00847 nsRenderingContextImpl::DrawString(const char *aString, PRUint32 aLength,
00848                                    nscoord aX, nscoord aY,
00849                                    const nscoord* aSpacing)
00850 {
00851   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00852   while (aLength > 0) {
00853     PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength);
00854     nsresult rv = DrawStringInternal(aString, len, aX, aY);
00855     if (NS_FAILED(rv))
00856       return rv;
00857     aLength -= len;
00858 
00859     if (aLength > 0) {
00860       nscoord width;
00861       rv = GetWidthInternal(aString, len, width);
00862       if (NS_FAILED(rv))
00863         return rv;
00864       aX += width;
00865       aString += len;
00866     }
00867   }
00868   return NS_OK;
00869 }
00870 
00871 NS_IMETHODIMP
00872 nsRenderingContextImpl::DrawString(const PRUnichar *aString, PRUint32 aLength,
00873                                    nscoord aX, nscoord aY,
00874                                    PRInt32 aFontID,
00875                                    const nscoord* aSpacing)
00876 {
00877   PRUint32 maxChunkLength = GetMaxChunkLength(this);
00878   if (aLength <= maxChunkLength) {
00879     return DrawStringInternal(aString, aLength, aX, aY, aFontID, aSpacing);
00880   }
00881 
00882   PRBool isRTL = PR_FALSE;
00883   GetRightToLeftText(&isRTL);
00884 
00885   if (isRTL) {
00886     nscoord totalWidth = 0;
00887     if (aSpacing) {
00888       for (PRUint32 i = 0; i < aLength; ++i) {
00889         totalWidth += aSpacing[i];
00890       }
00891     } else {
00892       nsresult rv = GetWidth(aString, aLength, totalWidth);
00893       if (NS_FAILED(rv))
00894         return rv;
00895     }
00896     aX += totalWidth;
00897   }
00898   
00899   while (aLength > 0) {
00900     PRInt32 len = FindSafeLength(this, aString, aLength, maxChunkLength);
00901     nscoord width = 0;
00902     if (aSpacing) {
00903       for (PRInt32 i = 0; i < len; ++i) {
00904         width += aSpacing[i];
00905       }
00906     } else {
00907       nsresult rv = GetWidthInternal(aString, len, width);
00908       if (NS_FAILED(rv))
00909         return rv;
00910     }
00911 
00912     if (isRTL) {
00913       aX -= width;
00914     }
00915     nsresult rv = DrawStringInternal(aString, len, aX, aY, aFontID, aSpacing);
00916     if (NS_FAILED(rv))
00917       return rv;
00918     aLength -= len;
00919     if (!isRTL) {
00920       aX += width;
00921     }
00922     aString += len;
00923     if (aSpacing) {
00924       aSpacing += len;
00925     }
00926   }
00927   return NS_OK;
00928 }
00929 
00930 NS_IMETHODIMP
00931 nsRenderingContextImpl::RenderEPS(const nsRect& aRect, FILE *aDataFile)
00932 {
00933   return NS_ERROR_NOT_IMPLEMENTED;
00934 }