gfx/src/nsRenderingContext.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsRenderingContext.h"
michael@0 7 #include <string.h> // for strlen
michael@0 8 #include <algorithm> // for min
michael@0 9 #include "gfxColor.h" // for gfxRGBA
michael@0 10 #include "gfxMatrix.h" // for gfxMatrix
michael@0 11 #include "gfxPoint.h" // for gfxPoint, gfxSize
michael@0 12 #include "gfxRect.h" // for gfxRect
michael@0 13 #include "gfxTypes.h" // for gfxFloat
michael@0 14 #include "mozilla/gfx/BasePoint.h" // for BasePoint
michael@0 15 #include "mozilla/mozalloc.h" // for operator delete[], etc
michael@0 16 #include "nsBoundingMetrics.h" // for nsBoundingMetrics
michael@0 17 #include "nsCharTraits.h" // for NS_IS_LOW_SURROGATE
michael@0 18 #include "nsDebug.h" // for NS_ERROR
michael@0 19 #include "nsPoint.h" // for nsPoint
michael@0 20 #include "nsRect.h" // for nsRect, nsIntRect
michael@0 21 #include "nsRegion.h" // for nsIntRegionRectIterator, etc
michael@0 22
michael@0 23 class gfxASurface;
michael@0 24
michael@0 25 // XXXTodo: rename FORM_TWIPS to FROM_APPUNITS
michael@0 26 #define FROM_TWIPS(_x) ((gfxFloat)((_x)/(mP2A)))
michael@0 27 #define FROM_TWIPS_INT(_x) (NSToIntRound((gfxFloat)((_x)/(mP2A))))
michael@0 28 #define TO_TWIPS(_x) ((nscoord)((_x)*(mP2A)))
michael@0 29 #define GFX_RECT_FROM_TWIPS_RECT(_r) (gfxRect(FROM_TWIPS((_r).x), FROM_TWIPS((_r).y), FROM_TWIPS((_r).width), FROM_TWIPS((_r).height)))
michael@0 30
michael@0 31 // Hard limit substring lengths to 8000 characters ... this lets us statically
michael@0 32 // size the cluster buffer array in FindSafeLength
michael@0 33 #define MAX_GFX_TEXT_BUF_SIZE 8000
michael@0 34
michael@0 35 static int32_t FindSafeLength(const char16_t *aString, uint32_t aLength,
michael@0 36 uint32_t aMaxChunkLength)
michael@0 37 {
michael@0 38 if (aLength <= aMaxChunkLength)
michael@0 39 return aLength;
michael@0 40
michael@0 41 int32_t len = aMaxChunkLength;
michael@0 42
michael@0 43 // Ensure that we don't break inside a surrogate pair
michael@0 44 while (len > 0 && NS_IS_LOW_SURROGATE(aString[len])) {
michael@0 45 len--;
michael@0 46 }
michael@0 47 if (len == 0) {
michael@0 48 // We don't want our caller to go into an infinite loop, so don't
michael@0 49 // return zero. It's hard to imagine how we could actually get here
michael@0 50 // unless there are languages that allow clusters of arbitrary size.
michael@0 51 // If there are and someone feeds us a 500+ character cluster, too
michael@0 52 // bad.
michael@0 53 return aMaxChunkLength;
michael@0 54 }
michael@0 55 return len;
michael@0 56 }
michael@0 57
michael@0 58 static int32_t FindSafeLength(const char *aString, uint32_t aLength,
michael@0 59 uint32_t aMaxChunkLength)
michael@0 60 {
michael@0 61 // Since it's ASCII, we don't need to worry about clusters or RTL
michael@0 62 return std::min(aLength, aMaxChunkLength);
michael@0 63 }
michael@0 64
michael@0 65 //////////////////////////////////////////////////////////////////////
michael@0 66 //// nsRenderingContext
michael@0 67
michael@0 68 void
michael@0 69 nsRenderingContext::Init(nsDeviceContext* aContext,
michael@0 70 gfxASurface *aThebesSurface)
michael@0 71 {
michael@0 72 Init(aContext, new gfxContext(aThebesSurface));
michael@0 73 }
michael@0 74
michael@0 75 void
michael@0 76 nsRenderingContext::Init(nsDeviceContext* aContext,
michael@0 77 gfxContext *aThebesContext)
michael@0 78 {
michael@0 79 mDeviceContext = aContext;
michael@0 80 mThebes = aThebesContext;
michael@0 81
michael@0 82 mThebes->SetLineWidth(1.0);
michael@0 83 mP2A = mDeviceContext->AppUnitsPerDevPixel();
michael@0 84 }
michael@0 85
michael@0 86 void
michael@0 87 nsRenderingContext::Init(nsDeviceContext* aContext,
michael@0 88 DrawTarget *aDrawTarget)
michael@0 89 {
michael@0 90 Init(aContext, new gfxContext(aDrawTarget));
michael@0 91 }
michael@0 92
michael@0 93 //
michael@0 94 // graphics state
michael@0 95 //
michael@0 96
michael@0 97 void
michael@0 98 nsRenderingContext::PushState()
michael@0 99 {
michael@0 100 mThebes->Save();
michael@0 101 }
michael@0 102
michael@0 103 void
michael@0 104 nsRenderingContext::PopState()
michael@0 105 {
michael@0 106 mThebes->Restore();
michael@0 107 }
michael@0 108
michael@0 109 void
michael@0 110 nsRenderingContext::IntersectClip(const nsRect& aRect)
michael@0 111 {
michael@0 112 mThebes->NewPath();
michael@0 113 gfxRect clipRect(GFX_RECT_FROM_TWIPS_RECT(aRect));
michael@0 114 if (mThebes->UserToDevicePixelSnapped(clipRect, true)) {
michael@0 115 gfxMatrix mat(mThebes->CurrentMatrix());
michael@0 116 mat.Invert();
michael@0 117 clipRect = mat.Transform(clipRect);
michael@0 118 mThebes->Rectangle(clipRect);
michael@0 119 } else {
michael@0 120 mThebes->Rectangle(clipRect);
michael@0 121 }
michael@0 122
michael@0 123 mThebes->Clip();
michael@0 124 }
michael@0 125
michael@0 126 void
michael@0 127 nsRenderingContext::SetClip(const nsIntRegion& aRegion)
michael@0 128 {
michael@0 129 // Region is in device coords, no transformation. This should
michael@0 130 // only be called when there is no transform in place, when we we
michael@0 131 // just start painting a widget. The region is set by the platform
michael@0 132 // paint routine. Therefore, there is no option to intersect with
michael@0 133 // an existing clip.
michael@0 134
michael@0 135 gfxMatrix mat = mThebes->CurrentMatrix();
michael@0 136 mThebes->IdentityMatrix();
michael@0 137
michael@0 138 mThebes->ResetClip();
michael@0 139
michael@0 140 mThebes->NewPath();
michael@0 141 nsIntRegionRectIterator iter(aRegion);
michael@0 142 const nsIntRect* rect;
michael@0 143 while ((rect = iter.Next())) {
michael@0 144 mThebes->Rectangle(gfxRect(rect->x, rect->y, rect->width, rect->height),
michael@0 145 true);
michael@0 146 }
michael@0 147 mThebes->Clip();
michael@0 148 mThebes->SetMatrix(mat);
michael@0 149 }
michael@0 150
michael@0 151 void
michael@0 152 nsRenderingContext::SetLineStyle(nsLineStyle aLineStyle)
michael@0 153 {
michael@0 154 switch (aLineStyle) {
michael@0 155 case nsLineStyle_kSolid:
michael@0 156 mThebes->SetDash(gfxContext::gfxLineSolid);
michael@0 157 break;
michael@0 158 case nsLineStyle_kDashed:
michael@0 159 mThebes->SetDash(gfxContext::gfxLineDashed);
michael@0 160 break;
michael@0 161 case nsLineStyle_kDotted:
michael@0 162 mThebes->SetDash(gfxContext::gfxLineDotted);
michael@0 163 break;
michael@0 164 case nsLineStyle_kNone:
michael@0 165 default:
michael@0 166 // nothing uses kNone
michael@0 167 NS_ERROR("SetLineStyle: Invalid line style");
michael@0 168 break;
michael@0 169 }
michael@0 170 }
michael@0 171
michael@0 172
michael@0 173 void
michael@0 174 nsRenderingContext::SetColor(nscolor aColor)
michael@0 175 {
michael@0 176 /* This sets the color assuming the sRGB color space, since that's
michael@0 177 * what all CSS colors are defined to be in by the spec.
michael@0 178 */
michael@0 179 mThebes->SetColor(gfxRGBA(aColor));
michael@0 180 }
michael@0 181
michael@0 182 void
michael@0 183 nsRenderingContext::Translate(const nsPoint& aPt)
michael@0 184 {
michael@0 185 mThebes->Translate(gfxPoint(FROM_TWIPS(aPt.x), FROM_TWIPS(aPt.y)));
michael@0 186 }
michael@0 187
michael@0 188 void
michael@0 189 nsRenderingContext::Scale(float aSx, float aSy)
michael@0 190 {
michael@0 191 mThebes->Scale(aSx, aSy);
michael@0 192 }
michael@0 193
michael@0 194 //
michael@0 195 // shapes
michael@0 196 //
michael@0 197
michael@0 198 void
michael@0 199 nsRenderingContext::DrawLine(const nsPoint& aStartPt, const nsPoint& aEndPt)
michael@0 200 {
michael@0 201 DrawLine(aStartPt.x, aStartPt.y, aEndPt.x, aEndPt.y);
michael@0 202 }
michael@0 203
michael@0 204 void
michael@0 205 nsRenderingContext::DrawLine(nscoord aX0, nscoord aY0,
michael@0 206 nscoord aX1, nscoord aY1)
michael@0 207 {
michael@0 208 gfxPoint p0 = gfxPoint(FROM_TWIPS(aX0), FROM_TWIPS(aY0));
michael@0 209 gfxPoint p1 = gfxPoint(FROM_TWIPS(aX1), FROM_TWIPS(aY1));
michael@0 210
michael@0 211 // we can't draw thick lines with gfx, so we always assume we want
michael@0 212 // pixel-aligned lines if the rendering context is at 1.0 scale
michael@0 213 gfxMatrix savedMatrix = mThebes->CurrentMatrix();
michael@0 214 if (!savedMatrix.HasNonTranslation()) {
michael@0 215 p0 = mThebes->UserToDevice(p0);
michael@0 216 p1 = mThebes->UserToDevice(p1);
michael@0 217
michael@0 218 p0.Round();
michael@0 219 p1.Round();
michael@0 220
michael@0 221 mThebes->IdentityMatrix();
michael@0 222
michael@0 223 mThebes->NewPath();
michael@0 224
michael@0 225 // snap straight lines
michael@0 226 if (p0.x == p1.x) {
michael@0 227 mThebes->Line(p0 + gfxPoint(0.5, 0),
michael@0 228 p1 + gfxPoint(0.5, 0));
michael@0 229 } else if (p0.y == p1.y) {
michael@0 230 mThebes->Line(p0 + gfxPoint(0, 0.5),
michael@0 231 p1 + gfxPoint(0, 0.5));
michael@0 232 } else {
michael@0 233 mThebes->Line(p0, p1);
michael@0 234 }
michael@0 235
michael@0 236 mThebes->Stroke();
michael@0 237
michael@0 238 mThebes->SetMatrix(savedMatrix);
michael@0 239 } else {
michael@0 240 mThebes->NewPath();
michael@0 241 mThebes->Line(p0, p1);
michael@0 242 mThebes->Stroke();
michael@0 243 }
michael@0 244 }
michael@0 245
michael@0 246 void
michael@0 247 nsRenderingContext::DrawRect(const nsRect& aRect)
michael@0 248 {
michael@0 249 mThebes->NewPath();
michael@0 250 mThebes->Rectangle(GFX_RECT_FROM_TWIPS_RECT(aRect), true);
michael@0 251 mThebes->Stroke();
michael@0 252 }
michael@0 253
michael@0 254 void
michael@0 255 nsRenderingContext::DrawRect(nscoord aX, nscoord aY,
michael@0 256 nscoord aWidth, nscoord aHeight)
michael@0 257 {
michael@0 258 DrawRect(nsRect(aX, aY, aWidth, aHeight));
michael@0 259 }
michael@0 260
michael@0 261
michael@0 262 /* Clamp r to (0,0) (2^23,2^23)
michael@0 263 * these are to be device coordinates.
michael@0 264 *
michael@0 265 * Returns false if the rectangle is completely out of bounds,
michael@0 266 * true otherwise.
michael@0 267 *
michael@0 268 * This function assumes that it will be called with a rectangle being
michael@0 269 * drawn into a surface with an identity transformation matrix; that
michael@0 270 * is, anything above or to the left of (0,0) will be offscreen.
michael@0 271 *
michael@0 272 * First it checks if the rectangle is entirely beyond
michael@0 273 * CAIRO_COORD_MAX; if so, it can't ever appear on the screen --
michael@0 274 * false is returned.
michael@0 275 *
michael@0 276 * Then it shifts any rectangles with x/y < 0 so that x and y are = 0,
michael@0 277 * and adjusts the width and height appropriately. For example, a
michael@0 278 * rectangle from (0,-5) with dimensions (5,10) will become a
michael@0 279 * rectangle from (0,0) with dimensions (5,5).
michael@0 280 *
michael@0 281 * If after negative x/y adjustment to 0, either the width or height
michael@0 282 * is negative, then the rectangle is completely offscreen, and
michael@0 283 * nothing is drawn -- false is returned.
michael@0 284 *
michael@0 285 * Finally, if x+width or y+height are greater than CAIRO_COORD_MAX,
michael@0 286 * the width and height are clamped such x+width or y+height are equal
michael@0 287 * to CAIRO_COORD_MAX, and true is returned.
michael@0 288 */
michael@0 289 #define CAIRO_COORD_MAX (double(0x7fffff))
michael@0 290
michael@0 291 static bool
michael@0 292 ConditionRect(gfxRect& r) {
michael@0 293 // if either x or y is way out of bounds;
michael@0 294 // note that we don't handle negative w/h here
michael@0 295 if (r.X() > CAIRO_COORD_MAX || r.Y() > CAIRO_COORD_MAX)
michael@0 296 return false;
michael@0 297
michael@0 298 if (r.X() < 0.0) {
michael@0 299 r.width += r.X();
michael@0 300 if (r.width < 0.0)
michael@0 301 return false;
michael@0 302 r.x = 0.0;
michael@0 303 }
michael@0 304
michael@0 305 if (r.XMost() > CAIRO_COORD_MAX) {
michael@0 306 r.width = CAIRO_COORD_MAX - r.X();
michael@0 307 }
michael@0 308
michael@0 309 if (r.Y() < 0.0) {
michael@0 310 r.height += r.Y();
michael@0 311 if (r.Height() < 0.0)
michael@0 312 return false;
michael@0 313
michael@0 314 r.y = 0.0;
michael@0 315 }
michael@0 316
michael@0 317 if (r.YMost() > CAIRO_COORD_MAX) {
michael@0 318 r.height = CAIRO_COORD_MAX - r.Y();
michael@0 319 }
michael@0 320 return true;
michael@0 321 }
michael@0 322
michael@0 323 void
michael@0 324 nsRenderingContext::FillRect(const nsRect& aRect)
michael@0 325 {
michael@0 326 gfxRect r(GFX_RECT_FROM_TWIPS_RECT(aRect));
michael@0 327
michael@0 328 /* Clamp coordinates to work around a design bug in cairo */
michael@0 329 nscoord bigval = (nscoord)(CAIRO_COORD_MAX*mP2A);
michael@0 330 if (aRect.width > bigval ||
michael@0 331 aRect.height > bigval ||
michael@0 332 aRect.x < -bigval ||
michael@0 333 aRect.x > bigval ||
michael@0 334 aRect.y < -bigval ||
michael@0 335 aRect.y > bigval)
michael@0 336 {
michael@0 337 gfxMatrix mat = mThebes->CurrentMatrix();
michael@0 338
michael@0 339 r = mat.Transform(r);
michael@0 340
michael@0 341 if (!ConditionRect(r))
michael@0 342 return;
michael@0 343
michael@0 344 mThebes->IdentityMatrix();
michael@0 345 mThebes->NewPath();
michael@0 346
michael@0 347 mThebes->Rectangle(r, true);
michael@0 348 mThebes->Fill();
michael@0 349 mThebes->SetMatrix(mat);
michael@0 350 }
michael@0 351
michael@0 352 mThebes->NewPath();
michael@0 353 mThebes->Rectangle(r, true);
michael@0 354 mThebes->Fill();
michael@0 355 }
michael@0 356
michael@0 357 void
michael@0 358 nsRenderingContext::FillRect(nscoord aX, nscoord aY,
michael@0 359 nscoord aWidth, nscoord aHeight)
michael@0 360 {
michael@0 361 FillRect(nsRect(aX, aY, aWidth, aHeight));
michael@0 362 }
michael@0 363
michael@0 364 void
michael@0 365 nsRenderingContext::InvertRect(const nsRect& aRect)
michael@0 366 {
michael@0 367 gfxContext::GraphicsOperator lastOp = mThebes->CurrentOperator();
michael@0 368
michael@0 369 mThebes->SetOperator(gfxContext::OPERATOR_XOR);
michael@0 370 FillRect(aRect);
michael@0 371 mThebes->SetOperator(lastOp);
michael@0 372 }
michael@0 373
michael@0 374 void
michael@0 375 nsRenderingContext::DrawEllipse(nscoord aX, nscoord aY,
michael@0 376 nscoord aWidth, nscoord aHeight)
michael@0 377 {
michael@0 378 mThebes->NewPath();
michael@0 379 mThebes->Ellipse(gfxPoint(FROM_TWIPS(aX) + FROM_TWIPS(aWidth)/2.0,
michael@0 380 FROM_TWIPS(aY) + FROM_TWIPS(aHeight)/2.0),
michael@0 381 gfxSize(FROM_TWIPS(aWidth),
michael@0 382 FROM_TWIPS(aHeight)));
michael@0 383 mThebes->Stroke();
michael@0 384 }
michael@0 385
michael@0 386 void
michael@0 387 nsRenderingContext::FillEllipse(const nsRect& aRect)
michael@0 388 {
michael@0 389 FillEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
michael@0 390 }
michael@0 391
michael@0 392 void
michael@0 393 nsRenderingContext::FillEllipse(nscoord aX, nscoord aY,
michael@0 394 nscoord aWidth, nscoord aHeight)
michael@0 395 {
michael@0 396 mThebes->NewPath();
michael@0 397 mThebes->Ellipse(gfxPoint(FROM_TWIPS(aX) + FROM_TWIPS(aWidth)/2.0,
michael@0 398 FROM_TWIPS(aY) + FROM_TWIPS(aHeight)/2.0),
michael@0 399 gfxSize(FROM_TWIPS(aWidth),
michael@0 400 FROM_TWIPS(aHeight)));
michael@0 401 mThebes->Fill();
michael@0 402 }
michael@0 403
michael@0 404 void
michael@0 405 nsRenderingContext::FillPolygon(const nsPoint twPoints[], int32_t aNumPoints)
michael@0 406 {
michael@0 407 if (aNumPoints == 0)
michael@0 408 return;
michael@0 409
michael@0 410 nsAutoArrayPtr<gfxPoint> pxPoints(new gfxPoint[aNumPoints]);
michael@0 411
michael@0 412 for (int i = 0; i < aNumPoints; i++) {
michael@0 413 pxPoints[i].x = FROM_TWIPS(twPoints[i].x);
michael@0 414 pxPoints[i].y = FROM_TWIPS(twPoints[i].y);
michael@0 415 }
michael@0 416
michael@0 417 mThebes->NewPath();
michael@0 418 mThebes->Polygon(pxPoints, aNumPoints);
michael@0 419 mThebes->Fill();
michael@0 420 }
michael@0 421
michael@0 422 //
michael@0 423 // text
michael@0 424 //
michael@0 425
michael@0 426 void
michael@0 427 nsRenderingContext::SetTextRunRTL(bool aIsRTL)
michael@0 428 {
michael@0 429 mFontMetrics->SetTextRunRTL(aIsRTL);
michael@0 430 }
michael@0 431
michael@0 432 void
michael@0 433 nsRenderingContext::SetFont(nsFontMetrics *aFontMetrics)
michael@0 434 {
michael@0 435 mFontMetrics = aFontMetrics;
michael@0 436 }
michael@0 437
michael@0 438 int32_t
michael@0 439 nsRenderingContext::GetMaxChunkLength()
michael@0 440 {
michael@0 441 if (!mFontMetrics)
michael@0 442 return 1;
michael@0 443 return std::min(mFontMetrics->GetMaxStringLength(), MAX_GFX_TEXT_BUF_SIZE);
michael@0 444 }
michael@0 445
michael@0 446 nscoord
michael@0 447 nsRenderingContext::GetWidth(char aC)
michael@0 448 {
michael@0 449 if (aC == ' ' && mFontMetrics) {
michael@0 450 return mFontMetrics->SpaceWidth();
michael@0 451 }
michael@0 452
michael@0 453 return GetWidth(&aC, 1);
michael@0 454 }
michael@0 455
michael@0 456 nscoord
michael@0 457 nsRenderingContext::GetWidth(char16_t aC)
michael@0 458 {
michael@0 459 return GetWidth(&aC, 1);
michael@0 460 }
michael@0 461
michael@0 462 nscoord
michael@0 463 nsRenderingContext::GetWidth(const nsString& aString)
michael@0 464 {
michael@0 465 return GetWidth(aString.get(), aString.Length());
michael@0 466 }
michael@0 467
michael@0 468 nscoord
michael@0 469 nsRenderingContext::GetWidth(const char* aString)
michael@0 470 {
michael@0 471 return GetWidth(aString, strlen(aString));
michael@0 472 }
michael@0 473
michael@0 474 nscoord
michael@0 475 nsRenderingContext::GetWidth(const char* aString, uint32_t aLength)
michael@0 476 {
michael@0 477 uint32_t maxChunkLength = GetMaxChunkLength();
michael@0 478 nscoord width = 0;
michael@0 479 while (aLength > 0) {
michael@0 480 int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
michael@0 481 width += mFontMetrics->GetWidth(aString, len, this);
michael@0 482 aLength -= len;
michael@0 483 aString += len;
michael@0 484 }
michael@0 485 return width;
michael@0 486 }
michael@0 487
michael@0 488 nscoord
michael@0 489 nsRenderingContext::GetWidth(const char16_t *aString, uint32_t aLength)
michael@0 490 {
michael@0 491 uint32_t maxChunkLength = GetMaxChunkLength();
michael@0 492 nscoord width = 0;
michael@0 493 while (aLength > 0) {
michael@0 494 int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
michael@0 495 width += mFontMetrics->GetWidth(aString, len, this);
michael@0 496 aLength -= len;
michael@0 497 aString += len;
michael@0 498 }
michael@0 499 return width;
michael@0 500 }
michael@0 501
michael@0 502 nsBoundingMetrics
michael@0 503 nsRenderingContext::GetBoundingMetrics(const char16_t* aString,
michael@0 504 uint32_t aLength)
michael@0 505 {
michael@0 506 uint32_t maxChunkLength = GetMaxChunkLength();
michael@0 507 int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
michael@0 508 // Assign directly in the first iteration. This ensures that
michael@0 509 // negative ascent/descent can be returned and the left bearing
michael@0 510 // is properly initialized.
michael@0 511 nsBoundingMetrics totalMetrics
michael@0 512 = mFontMetrics->GetBoundingMetrics(aString, len, this);
michael@0 513 aLength -= len;
michael@0 514 aString += len;
michael@0 515
michael@0 516 while (aLength > 0) {
michael@0 517 len = FindSafeLength(aString, aLength, maxChunkLength);
michael@0 518 nsBoundingMetrics metrics
michael@0 519 = mFontMetrics->GetBoundingMetrics(aString, len, this);
michael@0 520 totalMetrics += metrics;
michael@0 521 aLength -= len;
michael@0 522 aString += len;
michael@0 523 }
michael@0 524 return totalMetrics;
michael@0 525 }
michael@0 526
michael@0 527 void
michael@0 528 nsRenderingContext::DrawString(const char *aString, uint32_t aLength,
michael@0 529 nscoord aX, nscoord aY)
michael@0 530 {
michael@0 531 uint32_t maxChunkLength = GetMaxChunkLength();
michael@0 532 while (aLength > 0) {
michael@0 533 int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
michael@0 534 mFontMetrics->DrawString(aString, len, aX, aY, this);
michael@0 535 aLength -= len;
michael@0 536
michael@0 537 if (aLength > 0) {
michael@0 538 nscoord width = mFontMetrics->GetWidth(aString, len, this);
michael@0 539 aX += width;
michael@0 540 aString += len;
michael@0 541 }
michael@0 542 }
michael@0 543 }
michael@0 544
michael@0 545 void
michael@0 546 nsRenderingContext::DrawString(const nsString& aString, nscoord aX, nscoord aY)
michael@0 547 {
michael@0 548 DrawString(aString.get(), aString.Length(), aX, aY);
michael@0 549 }
michael@0 550
michael@0 551 void
michael@0 552 nsRenderingContext::DrawString(const char16_t *aString, uint32_t aLength,
michael@0 553 nscoord aX, nscoord aY)
michael@0 554 {
michael@0 555 uint32_t maxChunkLength = GetMaxChunkLength();
michael@0 556 if (aLength <= maxChunkLength) {
michael@0 557 mFontMetrics->DrawString(aString, aLength, aX, aY, this, this);
michael@0 558 return;
michael@0 559 }
michael@0 560
michael@0 561 bool isRTL = mFontMetrics->GetTextRunRTL();
michael@0 562
michael@0 563 // If we're drawing right to left, we must start at the end.
michael@0 564 if (isRTL) {
michael@0 565 aX += GetWidth(aString, aLength);
michael@0 566 }
michael@0 567
michael@0 568 while (aLength > 0) {
michael@0 569 int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
michael@0 570 nscoord width = mFontMetrics->GetWidth(aString, len, this);
michael@0 571 if (isRTL) {
michael@0 572 aX -= width;
michael@0 573 }
michael@0 574 mFontMetrics->DrawString(aString, len, aX, aY, this, this);
michael@0 575 if (!isRTL) {
michael@0 576 aX += width;
michael@0 577 }
michael@0 578 aLength -= len;
michael@0 579 aString += len;
michael@0 580 }
michael@0 581 }

mercurial