1 /* 2 * Copyright (c) 2006, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "GraphicsContext.h" 33 34 #include "AffineTransform.h" 35 #include "Color.h" 36 #include "FloatRect.h" 37 #include "GLES2Canvas.h" 38 #include "Gradient.h" 39 #include "GraphicsContextPlatformPrivate.h" 40 #include "ImageBuffer.h" 41 #include "IntRect.h" 42 #include "NativeImageSkia.h" 43 #include "NotImplemented.h" 44 #include "PlatformContextSkia.h" 45 46 #include "SkBitmap.h" 47 #include "SkBlurMaskFilter.h" 48 #include "SkColorFilter.h" 49 #include "SkCornerPathEffect.h" 50 #include "SkLayerDrawLooper.h" 51 #include "SkShader.h" 52 #include "SkiaUtils.h" 53 #include "skia/ext/platform_canvas.h" 54 55 #include <math.h> 56 #include <wtf/Assertions.h> 57 #include <wtf/MathExtras.h> 58 #include <wtf/UnusedParam.h> 59 60 using namespace std; 61 62 namespace WebCore { 63 64 namespace { 65 66 inline int fastMod(int value, int max) 67 { 68 int sign = SkExtractSign(value); 69 70 value = SkApplySign(value, sign); 71 if (value >= max) 72 value %= max; 73 return SkApplySign(value, sign); 74 } 75 76 inline float square(float n) 77 { 78 return n * n; 79 } 80 81 } // namespace 82 83 // "Seatbelt" functions ------------------------------------------------------ 84 // 85 // These functions check certain graphics primitives for being "safe". 86 // Skia has historically crashed when sent crazy data. These functions do 87 // additional checking to prevent crashes. 88 // 89 // Ideally, all of these would be fixed in the graphics layer and we would not 90 // have to do any checking. You can uncomment the ENSURE_VALUE_SAFETY_FOR_SKIA 91 // flag to check the graphics layer. 92 93 // Disabling these checks (20/01/2010), since we think we've fixed all the Skia 94 // bugs. Leaving the code in for now, so we can revert easily if necessary. 95 // #define ENSURE_VALUE_SAFETY_FOR_SKIA 96 97 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA 98 static bool isCoordinateSkiaSafe(float coord) 99 { 100 // First check for valid floats. 101 #if defined(_MSC_VER) 102 if (!_finite(coord)) 103 #else 104 if (!finite(coord)) 105 #endif 106 return false; 107 108 // Skia uses 16.16 fixed point and 26.6 fixed point in various places. If 109 // the transformed point exceeds 15 bits, we just declare that it's 110 // unreasonable to catch both of these cases. 111 static const int maxPointMagnitude = 32767; 112 if (coord > maxPointMagnitude || coord < -maxPointMagnitude) 113 return false; 114 115 return true; 116 } 117 #endif 118 119 static bool isPointSkiaSafe(const SkMatrix& transform, const SkPoint& pt) 120 { 121 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA 122 // Now check for points that will overflow. We check the *transformed* 123 // points since this is what will be rasterized. 124 SkPoint xPt; 125 transform.mapPoints(&xPt, &pt, 1); 126 return isCoordinateSkiaSafe(xPt.fX) && isCoordinateSkiaSafe(xPt.fY); 127 #else 128 return true; 129 #endif 130 } 131 132 static bool isRectSkiaSafe(const SkMatrix& transform, const SkRect& rc) 133 { 134 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA 135 SkPoint topleft = {rc.fLeft, rc.fTop}; 136 SkPoint bottomright = {rc.fRight, rc.fBottom}; 137 return isPointSkiaSafe(transform, topleft) && isPointSkiaSafe(transform, bottomright); 138 #else 139 return true; 140 #endif 141 } 142 143 bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path) 144 { 145 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA 146 SkPoint current_points[4]; 147 SkPath::Iter iter(path, false); 148 for (SkPath::Verb verb = iter.next(current_points); 149 verb != SkPath::kDone_Verb; 150 verb = iter.next(current_points)) { 151 switch (verb) { 152 case SkPath::kMove_Verb: 153 // This move will be duplicated in the next verb, so we can ignore. 154 break; 155 case SkPath::kLine_Verb: 156 // iter.next returns 2 points. 157 if (!isPointSkiaSafe(transform, current_points[0]) 158 || !isPointSkiaSafe(transform, current_points[1])) 159 return false; 160 break; 161 case SkPath::kQuad_Verb: 162 // iter.next returns 3 points. 163 if (!isPointSkiaSafe(transform, current_points[0]) 164 || !isPointSkiaSafe(transform, current_points[1]) 165 || !isPointSkiaSafe(transform, current_points[2])) 166 return false; 167 break; 168 case SkPath::kCubic_Verb: 169 // iter.next returns 4 points. 170 if (!isPointSkiaSafe(transform, current_points[0]) 171 || !isPointSkiaSafe(transform, current_points[1]) 172 || !isPointSkiaSafe(transform, current_points[2]) 173 || !isPointSkiaSafe(transform, current_points[3])) 174 return false; 175 break; 176 case SkPath::kClose_Verb: 177 case SkPath::kDone_Verb: 178 default: 179 break; 180 } 181 } 182 return true; 183 #else 184 return true; 185 #endif 186 } 187 188 // Local helper functions ------------------------------------------------------ 189 190 void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int startAngle) 191 { 192 SkIRect ir; 193 int rx = SkMin32(SkScalarRound(rect.width()), size.width()); 194 int ry = SkMin32(SkScalarRound(rect.height()), size.height()); 195 196 ir.set(-rx, -ry, rx, ry); 197 switch (startAngle) { 198 case 0: 199 ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom); 200 break; 201 case 90: 202 ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom); 203 break; 204 case 180: 205 ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop); 206 break; 207 case 270: 208 ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop); 209 break; 210 default: 211 ASSERT(0); 212 } 213 214 SkRect r; 215 r.set(ir); 216 path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false); 217 } 218 219 // ----------------------------------------------------------------------------- 220 221 // This may be called with a NULL pointer to create a graphics context that has 222 // no painting. 223 void GraphicsContext::platformInit(PlatformGraphicsContext* gc) 224 { 225 m_data = new GraphicsContextPlatformPrivate(gc); 226 setPaintingDisabled(!gc || !platformContext()->canvas()); 227 } 228 229 void GraphicsContext::platformDestroy() 230 { 231 delete m_data; 232 } 233 234 PlatformGraphicsContext* GraphicsContext::platformContext() const 235 { 236 ASSERT(!paintingDisabled()); 237 return m_data->context(); 238 } 239 240 // State saving ---------------------------------------------------------------- 241 242 void GraphicsContext::savePlatformState() 243 { 244 if (paintingDisabled()) 245 return; 246 247 if (platformContext()->useGPU()) 248 platformContext()->gpuCanvas()->save(); 249 250 // Save our private State. 251 platformContext()->save(); 252 } 253 254 void GraphicsContext::restorePlatformState() 255 { 256 if (paintingDisabled()) 257 return; 258 259 if (platformContext()->useGPU()) 260 platformContext()->gpuCanvas()->restore(); 261 262 // Restore our private State. 263 platformContext()->restore(); 264 } 265 266 void GraphicsContext::beginTransparencyLayer(float opacity) 267 { 268 if (paintingDisabled()) 269 return; 270 271 // We need the "alpha" layer flag here because the base layer is opaque 272 // (the surface of the page) but layers on top may have transparent parts. 273 // Without explicitly setting the alpha flag, the layer will inherit the 274 // opaque setting of the base and some things won't work properly. 275 platformContext()->canvas()->saveLayerAlpha( 276 0, 277 static_cast<unsigned char>(opacity * 255), 278 static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | 279 SkCanvas::kFullColorLayer_SaveFlag)); 280 } 281 282 void GraphicsContext::endTransparencyLayer() 283 { 284 if (paintingDisabled()) 285 return; 286 platformContext()->canvas()->restore(); 287 } 288 289 // Graphics primitives --------------------------------------------------------- 290 291 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) 292 { 293 if (paintingDisabled()) 294 return; 295 296 SkRect r(rect); 297 if (!isRectSkiaSafe(getCTM(), r)) 298 return; 299 300 platformContext()->prepareForSoftwareDraw(); 301 SkPath path; 302 path.addOval(r, SkPath::kCW_Direction); 303 // only perform the inset if we won't invert r 304 if (2 * thickness < rect.width() && 2 * thickness < rect.height()) { 305 // Adding one to the thickness doesn't make the border too thick as 306 // it's painted over afterwards. But without this adjustment the 307 // border appears a little anemic after anti-aliasing. 308 r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1)); 309 path.addOval(r, SkPath::kCCW_Direction); 310 } 311 platformContext()->clipPathAntiAliased(path); 312 } 313 314 void GraphicsContext::clearPlatformShadow() 315 { 316 if (paintingDisabled()) 317 return; 318 platformContext()->setDrawLooper(0); 319 } 320 321 void GraphicsContext::clearRect(const FloatRect& rect) 322 { 323 if (paintingDisabled()) 324 return; 325 326 if (platformContext()->useGPU() && !platformContext()->canvasClipApplied()) { 327 platformContext()->prepareForHardwareDraw(); 328 platformContext()->gpuCanvas()->clearRect(rect); 329 return; 330 } 331 332 // Force a readback here (if we're using the GPU), since clearRect() is 333 // incompatible with mixed-mode rendering. 334 platformContext()->syncSoftwareCanvas(); 335 336 SkRect r = rect; 337 if (!isRectSkiaSafe(getCTM(), r)) 338 ClipRectToCanvas(*platformContext()->canvas(), r, &r); 339 340 SkPaint paint; 341 platformContext()->setupPaintForFilling(&paint); 342 paint.setXfermodeMode(SkXfermode::kClear_Mode); 343 platformContext()->canvas()->drawRect(r, paint); 344 } 345 346 void GraphicsContext::clip(const FloatRect& rect) 347 { 348 if (paintingDisabled()) 349 return; 350 351 SkRect r(rect); 352 if (!isRectSkiaSafe(getCTM(), r)) 353 return; 354 355 platformContext()->prepareForSoftwareDraw(); 356 platformContext()->canvas()->clipRect(r); 357 } 358 359 void GraphicsContext::clip(const Path& path) 360 { 361 if (paintingDisabled()) 362 return; 363 364 const SkPath& p = *path.platformPath(); 365 if (!isPathSkiaSafe(getCTM(), p)) 366 return; 367 368 platformContext()->prepareForSoftwareDraw(); 369 platformContext()->clipPathAntiAliased(p); 370 } 371 372 void GraphicsContext::canvasClip(const Path& path) 373 { 374 if (paintingDisabled()) 375 return; 376 377 if (platformContext()->useGPU()) 378 platformContext()->gpuCanvas()->clipPath(path); 379 380 const SkPath& p = *path.platformPath(); 381 if (!isPathSkiaSafe(getCTM(), p)) 382 return; 383 384 platformContext()->canvasClipPath(p); 385 } 386 387 void GraphicsContext::clipOut(const IntRect& rect) 388 { 389 if (paintingDisabled()) 390 return; 391 392 SkRect r(rect); 393 if (!isRectSkiaSafe(getCTM(), r)) 394 return; 395 396 platformContext()->canvas()->clipRect(r, SkRegion::kDifference_Op); 397 } 398 399 void GraphicsContext::clipOut(const Path& p) 400 { 401 if (paintingDisabled()) 402 return; 403 404 if (platformContext()->useGPU()) 405 platformContext()->gpuCanvas()->clipOut(p); 406 407 const SkPath& path = *p.platformPath(); 408 if (!isPathSkiaSafe(getCTM(), path)) 409 return; 410 411 platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op); 412 } 413 414 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) 415 { 416 if (paintingDisabled()) 417 return; 418 419 if (platformContext()->useGPU()) 420 platformContext()->gpuCanvas()->clipPath(pathToClip); 421 422 SkPath path = *pathToClip.platformPath(); 423 if (!isPathSkiaSafe(getCTM(), path)) 424 return; 425 426 path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); 427 platformContext()->clipPathAntiAliased(path); 428 } 429 430 void GraphicsContext::concatCTM(const AffineTransform& affine) 431 { 432 if (paintingDisabled()) 433 return; 434 435 if (platformContext()->useGPU()) 436 platformContext()->gpuCanvas()->concatCTM(affine); 437 438 platformContext()->canvas()->concat(affine); 439 } 440 441 void GraphicsContext::setCTM(const AffineTransform& affine) 442 { 443 if (paintingDisabled()) 444 return; 445 446 if (platformContext()->useGPU()) 447 platformContext()->gpuCanvas()->setCTM(affine); 448 449 platformContext()->canvas()->setMatrix(affine); 450 } 451 452 void GraphicsContext::drawConvexPolygon(size_t numPoints, 453 const FloatPoint* points, 454 bool shouldAntialias) 455 { 456 if (paintingDisabled()) 457 return; 458 459 if (numPoints <= 1) 460 return; 461 462 platformContext()->prepareForSoftwareDraw(); 463 464 SkPath path; 465 466 path.incReserve(numPoints); 467 path.moveTo(WebCoreFloatToSkScalar(points[0].x()), 468 WebCoreFloatToSkScalar(points[0].y())); 469 for (size_t i = 1; i < numPoints; i++) { 470 path.lineTo(WebCoreFloatToSkScalar(points[i].x()), 471 WebCoreFloatToSkScalar(points[i].y())); 472 } 473 474 if (!isPathSkiaSafe(getCTM(), path)) 475 return; 476 477 SkPaint paint; 478 platformContext()->setupPaintForFilling(&paint); 479 platformContext()->canvas()->drawPath(path, paint); 480 481 if (strokeStyle() != NoStroke) { 482 paint.reset(); 483 platformContext()->setupPaintForStroking(&paint, 0, 0); 484 platformContext()->canvas()->drawPath(path, paint); 485 } 486 } 487 488 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) 489 { 490 if (paintingDisabled()) 491 return; 492 493 if (numPoints <= 1) 494 return; 495 496 // FIXME: IMPLEMENT!! 497 } 498 499 // This method is only used to draw the little circles used in lists. 500 void GraphicsContext::drawEllipse(const IntRect& elipseRect) 501 { 502 if (paintingDisabled()) 503 return; 504 505 SkRect rect = elipseRect; 506 if (!isRectSkiaSafe(getCTM(), rect)) 507 return; 508 509 platformContext()->prepareForSoftwareDraw(); 510 SkPaint paint; 511 platformContext()->setupPaintForFilling(&paint); 512 platformContext()->canvas()->drawOval(rect, paint); 513 514 if (strokeStyle() != NoStroke) { 515 paint.reset(); 516 platformContext()->setupPaintForStroking(&paint, &rect, 0); 517 platformContext()->canvas()->drawOval(rect, paint); 518 } 519 } 520 521 void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color) 522 { 523 // FIXME: implement 524 } 525 526 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color) 527 { 528 if (paintingDisabled()) 529 return; 530 531 unsigned rectCount = rects.size(); 532 if (!rectCount) 533 return; 534 535 platformContext()->prepareForSoftwareDraw(); 536 SkRegion focusRingRegion; 537 const SkScalar focusRingOutset = WebCoreFloatToSkScalar(0.5); 538 for (unsigned i = 0; i < rectCount; i++) { 539 SkIRect r = rects[i]; 540 r.inset(-focusRingOutset, -focusRingOutset); 541 focusRingRegion.op(r, SkRegion::kUnion_Op); 542 } 543 544 SkPath path; 545 SkPaint paint; 546 paint.setAntiAlias(true); 547 paint.setStyle(SkPaint::kStroke_Style); 548 549 paint.setColor(color.rgb()); 550 paint.setStrokeWidth(focusRingOutset * 2); 551 paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref(); 552 focusRingRegion.getBoundaryPath(&path); 553 platformContext()->canvas()->drawPath(path, paint); 554 } 555 556 // This is only used to draw borders. 557 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) 558 { 559 if (paintingDisabled()) 560 return; 561 562 StrokeStyle penStyle = strokeStyle(); 563 if (penStyle == NoStroke) 564 return; 565 566 SkPaint paint; 567 if (!isPointSkiaSafe(getCTM(), point1) || !isPointSkiaSafe(getCTM(), point2)) 568 return; 569 570 platformContext()->prepareForSoftwareDraw(); 571 572 FloatPoint p1 = point1; 573 FloatPoint p2 = point2; 574 bool isVerticalLine = (p1.x() == p2.x()); 575 int width = roundf(strokeThickness()); 576 577 // We know these are vertical or horizontal lines, so the length will just 578 // be the sum of the displacement component vectors give or take 1 - 579 // probably worth the speed up of no square root, which also won't be exact. 580 FloatSize disp = p2 - p1; 581 int length = SkScalarRound(disp.width() + disp.height()); 582 platformContext()->setupPaintForStroking(&paint, 0, length); 583 584 if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { 585 // Do a rect fill of our endpoints. This ensures we always have the 586 // appearance of being a border. We then draw the actual dotted/dashed line. 587 588 SkRect r1, r2; 589 r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width); 590 r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width); 591 592 if (isVerticalLine) { 593 r1.offset(-width / 2, 0); 594 r2.offset(-width / 2, -width); 595 } else { 596 r1.offset(0, -width / 2); 597 r2.offset(-width, -width / 2); 598 } 599 SkPaint fillPaint; 600 fillPaint.setColor(paint.getColor()); 601 platformContext()->canvas()->drawRect(r1, fillPaint); 602 platformContext()->canvas()->drawRect(r2, fillPaint); 603 } 604 605 adjustLineToPixelBoundaries(p1, p2, width, penStyle); 606 SkPoint pts[2] = { (SkPoint)p1, (SkPoint)p2 }; 607 608 platformContext()->canvas()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint); 609 } 610 611 void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, TextCheckingLineStyle style) 612 { 613 if (paintingDisabled()) 614 return; 615 616 platformContext()->prepareForSoftwareDraw(); 617 618 // Create the pattern we'll use to draw the underline. 619 static SkBitmap* misspellBitmap = 0; 620 if (!misspellBitmap) { 621 // We use a 2-pixel-high misspelling indicator because that seems to be 622 // what WebKit is designed for, and how much room there is in a typical 623 // page for it. 624 const int rowPixels = 32; // Must be multiple of 4 for pattern below. 625 const int colPixels = 2; 626 misspellBitmap = new SkBitmap; 627 misspellBitmap->setConfig(SkBitmap::kARGB_8888_Config, 628 rowPixels, colPixels); 629 misspellBitmap->allocPixels(); 630 631 misspellBitmap->eraseARGB(0, 0, 0, 0); 632 const uint32_t lineColor = 0xFFFF0000; // Opaque red. 633 const uint32_t antiColor = 0x60600000; // Semitransparent red. 634 635 // Pattern: X o o X o o X 636 // o X o o X o 637 uint32_t* row1 = misspellBitmap->getAddr32(0, 0); 638 uint32_t* row2 = misspellBitmap->getAddr32(0, 1); 639 for (int x = 0; x < rowPixels; x++) { 640 switch (x % 4) { 641 case 0: 642 row1[x] = lineColor; 643 break; 644 case 1: 645 row1[x] = antiColor; 646 row2[x] = antiColor; 647 break; 648 case 2: 649 row2[x] = lineColor; 650 break; 651 case 3: 652 row1[x] = antiColor; 653 row2[x] = antiColor; 654 break; 655 } 656 } 657 } 658 659 // Offset it vertically by 1 so that there's some space under the text. 660 SkScalar originX = WebCoreFloatToSkScalar(pt.x()); 661 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1; 662 663 // Make a shader for the bitmap with an origin of the box we'll draw. This 664 // shader is refcounted and will have an initial refcount of 1. 665 SkShader* shader = SkShader::CreateBitmapShader( 666 *misspellBitmap, SkShader::kRepeat_TileMode, 667 SkShader::kRepeat_TileMode); 668 SkMatrix matrix; 669 matrix.reset(); 670 matrix.postTranslate(originX, originY); 671 shader->setLocalMatrix(matrix); 672 673 // Assign the shader to the paint & release our reference. The paint will 674 // now own the shader and the shader will be destroyed when the paint goes 675 // out of scope. 676 SkPaint paint; 677 paint.setShader(shader); 678 shader->unref(); 679 680 SkRect rect; 681 rect.set(originX, 682 originY, 683 originX + WebCoreFloatToSkScalar(width), 684 originY + SkIntToScalar(misspellBitmap->height())); 685 platformContext()->canvas()->drawRect(rect, paint); 686 } 687 688 void GraphicsContext::drawLineForText(const FloatPoint& pt, 689 float width, 690 bool printing) 691 { 692 if (paintingDisabled()) 693 return; 694 695 if (width <= 0) 696 return; 697 698 platformContext()->prepareForSoftwareDraw(); 699 700 int thickness = SkMax32(static_cast<int>(strokeThickness()), 1); 701 SkRect r; 702 r.fLeft = WebCoreFloatToSkScalar(pt.x()); 703 r.fTop = WebCoreFloatToSkScalar(pt.y()); 704 r.fRight = r.fLeft + WebCoreFloatToSkScalar(width); 705 r.fBottom = r.fTop + SkIntToScalar(thickness); 706 707 SkPaint paint; 708 platformContext()->setupPaintForFilling(&paint); 709 // Text lines are drawn using the stroke color. 710 paint.setColor(platformContext()->effectiveStrokeColor()); 711 platformContext()->canvas()->drawRect(r, paint); 712 } 713 714 // Draws a filled rectangle with a stroked border. 715 void GraphicsContext::drawRect(const IntRect& rect) 716 { 717 if (paintingDisabled()) 718 return; 719 720 platformContext()->prepareForSoftwareDraw(); 721 722 SkRect r = rect; 723 if (!isRectSkiaSafe(getCTM(), r)) { 724 // See the fillRect below. 725 ClipRectToCanvas(*platformContext()->canvas(), r, &r); 726 } 727 728 platformContext()->drawRect(r); 729 } 730 731 void GraphicsContext::fillPath(const Path& pathToFill) 732 { 733 if (paintingDisabled()) 734 return; 735 736 // FIXME: add support to GLES2Canvas for more than just solid fills. 737 if (platformContext()->useGPU() && platformContext()->canAccelerate()) { 738 platformContext()->prepareForHardwareDraw(); 739 platformContext()->gpuCanvas()->fillPath(pathToFill); 740 return; 741 } 742 743 SkPath path = *pathToFill.platformPath(); 744 if (!isPathSkiaSafe(getCTM(), path)) 745 return; 746 747 platformContext()->prepareForSoftwareDraw(); 748 749 const GraphicsContextState& state = m_state; 750 path.setFillType(state.fillRule == RULE_EVENODD ? 751 SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); 752 753 SkPaint paint; 754 platformContext()->setupPaintForFilling(&paint); 755 756 platformContext()->canvas()->drawPath(path, paint); 757 } 758 759 void GraphicsContext::fillRect(const FloatRect& rect) 760 { 761 if (paintingDisabled()) 762 return; 763 764 SkRect r = rect; 765 if (!isRectSkiaSafe(getCTM(), r)) { 766 // See the other version of fillRect below. 767 ClipRectToCanvas(*platformContext()->canvas(), r, &r); 768 } 769 770 if (platformContext()->useGPU() && platformContext()->canAccelerate()) { 771 platformContext()->prepareForHardwareDraw(); 772 platformContext()->gpuCanvas()->fillRect(rect); 773 return; 774 } 775 776 platformContext()->save(); 777 778 platformContext()->prepareForSoftwareDraw(); 779 780 SkPaint paint; 781 platformContext()->setupPaintForFilling(&paint); 782 platformContext()->canvas()->drawRect(r, paint); 783 784 platformContext()->restore(); 785 } 786 787 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) 788 { 789 if (paintingDisabled()) 790 return; 791 792 if (platformContext()->useGPU() && platformContext()->canAccelerate()) { 793 platformContext()->prepareForHardwareDraw(); 794 platformContext()->gpuCanvas()->fillRect(rect, color, colorSpace); 795 return; 796 } 797 798 platformContext()->prepareForSoftwareDraw(); 799 800 SkRect r = rect; 801 if (!isRectSkiaSafe(getCTM(), r)) { 802 // Special case when the rectangle overflows fixed point. This is a 803 // workaround to fix bug 1212844. When the input rectangle is very 804 // large, it can overflow Skia's internal fixed point rect. This 805 // should be fixable in Skia (since the output bitmap isn't that 806 // large), but until that is fixed, we try to handle it ourselves. 807 // 808 // We manually clip the rectangle to the current clip rect. This 809 // will prevent overflow. The rectangle will be transformed to the 810 // canvas' coordinate space before it is converted to fixed point 811 // so we are guaranteed not to overflow after doing this. 812 ClipRectToCanvas(*platformContext()->canvas(), r, &r); 813 } 814 815 SkPaint paint; 816 platformContext()->setupPaintCommon(&paint); 817 paint.setColor(color.rgb()); 818 platformContext()->canvas()->drawRect(r, paint); 819 } 820 821 void GraphicsContext::fillRoundedRect(const IntRect& rect, 822 const IntSize& topLeft, 823 const IntSize& topRight, 824 const IntSize& bottomLeft, 825 const IntSize& bottomRight, 826 const Color& color, 827 ColorSpace colorSpace) 828 { 829 if (paintingDisabled()) 830 return; 831 832 platformContext()->prepareForSoftwareDraw(); 833 834 SkRect r = rect; 835 if (!isRectSkiaSafe(getCTM(), r)) 836 // See fillRect(). 837 ClipRectToCanvas(*platformContext()->canvas(), r, &r); 838 839 if (topLeft.width() + topRight.width() > rect.width() 840 || bottomLeft.width() + bottomRight.width() > rect.width() 841 || topLeft.height() + bottomLeft.height() > rect.height() 842 || topRight.height() + bottomRight.height() > rect.height()) { 843 // Not all the radii fit, return a rect. This matches the behavior of 844 // Path::createRoundedRectangle. Without this we attempt to draw a round 845 // shadow for a square box. 846 fillRect(rect, color, colorSpace); 847 return; 848 } 849 850 SkPath path; 851 addCornerArc(&path, r, topRight, 270); 852 addCornerArc(&path, r, bottomRight, 0); 853 addCornerArc(&path, r, bottomLeft, 90); 854 addCornerArc(&path, r, topLeft, 180); 855 856 SkPaint paint; 857 platformContext()->setupPaintForFilling(&paint); 858 paint.setColor(color.rgb()); 859 platformContext()->canvas()->drawPath(path, paint); 860 } 861 862 AffineTransform GraphicsContext::getCTM() const 863 { 864 const SkMatrix& m = platformContext()->canvas()->getTotalMatrix(); 865 return AffineTransform(SkScalarToDouble(m.getScaleX()), 866 SkScalarToDouble(m.getSkewY()), 867 SkScalarToDouble(m.getSkewX()), 868 SkScalarToDouble(m.getScaleY()), 869 SkScalarToDouble(m.getTranslateX()), 870 SkScalarToDouble(m.getTranslateY())); 871 } 872 873 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode) 874 { 875 return rect; 876 } 877 878 void GraphicsContext::scale(const FloatSize& size) 879 { 880 if (paintingDisabled()) 881 return; 882 883 if (platformContext()->useGPU()) 884 platformContext()->gpuCanvas()->scale(size); 885 886 platformContext()->canvas()->scale(WebCoreFloatToSkScalar(size.width()), 887 WebCoreFloatToSkScalar(size.height())); 888 } 889 890 void GraphicsContext::setAlpha(float alpha) 891 { 892 if (paintingDisabled()) 893 return; 894 895 if (platformContext()->useGPU()) 896 platformContext()->gpuCanvas()->setAlpha(alpha); 897 898 platformContext()->setAlpha(alpha); 899 } 900 901 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) 902 { 903 if (paintingDisabled()) 904 return; 905 906 if (platformContext()->useGPU()) 907 platformContext()->gpuCanvas()->setCompositeOperation(op); 908 909 platformContext()->setXfermodeMode(WebCoreCompositeToSkiaComposite(op)); 910 } 911 912 InterpolationQuality GraphicsContext::imageInterpolationQuality() const 913 { 914 return platformContext()->interpolationQuality(); 915 } 916 917 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality q) 918 { 919 platformContext()->setInterpolationQuality(q); 920 } 921 922 void GraphicsContext::setLineCap(LineCap cap) 923 { 924 if (paintingDisabled()) 925 return; 926 switch (cap) { 927 case ButtCap: 928 platformContext()->setLineCap(SkPaint::kButt_Cap); 929 break; 930 case RoundCap: 931 platformContext()->setLineCap(SkPaint::kRound_Cap); 932 break; 933 case SquareCap: 934 platformContext()->setLineCap(SkPaint::kSquare_Cap); 935 break; 936 default: 937 ASSERT(0); 938 break; 939 } 940 } 941 942 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) 943 { 944 if (paintingDisabled()) 945 return; 946 947 // FIXME: This is lifted directly off SkiaSupport, lines 49-74 948 // so it is not guaranteed to work correctly. 949 size_t dashLength = dashes.size(); 950 if (!dashLength) { 951 // If no dash is set, revert to solid stroke 952 // FIXME: do we need to set NoStroke in some cases? 953 platformContext()->setStrokeStyle(SolidStroke); 954 platformContext()->setDashPathEffect(0); 955 return; 956 } 957 958 size_t count = !(dashLength % 2) ? dashLength : dashLength * 2; 959 SkScalar* intervals = new SkScalar[count]; 960 961 for (unsigned int i = 0; i < count; i++) 962 intervals[i] = dashes[i % dashLength]; 963 964 platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset)); 965 966 delete[] intervals; 967 } 968 969 void GraphicsContext::setLineJoin(LineJoin join) 970 { 971 if (paintingDisabled()) 972 return; 973 switch (join) { 974 case MiterJoin: 975 platformContext()->setLineJoin(SkPaint::kMiter_Join); 976 break; 977 case RoundJoin: 978 platformContext()->setLineJoin(SkPaint::kRound_Join); 979 break; 980 case BevelJoin: 981 platformContext()->setLineJoin(SkPaint::kBevel_Join); 982 break; 983 default: 984 ASSERT(0); 985 break; 986 } 987 } 988 989 void GraphicsContext::setMiterLimit(float limit) 990 { 991 if (paintingDisabled()) 992 return; 993 platformContext()->setMiterLimit(limit); 994 } 995 996 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace) 997 { 998 if (paintingDisabled()) 999 return; 1000 1001 if (platformContext()->useGPU()) 1002 platformContext()->gpuCanvas()->setFillColor(color, colorSpace); 1003 1004 platformContext()->setFillColor(color.rgb()); 1005 } 1006 1007 void GraphicsContext::setPlatformFillGradient(Gradient* gradient) 1008 { 1009 if (paintingDisabled()) 1010 return; 1011 1012 platformContext()->setFillShader(gradient->platformGradient()); 1013 } 1014 1015 void GraphicsContext::setPlatformFillPattern(Pattern* pattern) 1016 { 1017 if (paintingDisabled()) 1018 return; 1019 1020 platformContext()->setFillShader(pattern->platformPattern(getCTM())); 1021 } 1022 1023 void GraphicsContext::setPlatformShadow(const FloatSize& size, 1024 float blurFloat, 1025 const Color& color, 1026 ColorSpace colorSpace) 1027 { 1028 if (paintingDisabled()) 1029 return; 1030 1031 if (platformContext()->useGPU()) { 1032 GLES2Canvas* canvas = platformContext()->gpuCanvas(); 1033 canvas->setShadowOffset(size); 1034 canvas->setShadowBlur(blurFloat); 1035 canvas->setShadowColor(color, colorSpace); 1036 canvas->setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms); 1037 } 1038 1039 // Detect when there's no effective shadow and clear the looper. 1040 if (!size.width() && !size.height() && !blurFloat) { 1041 platformContext()->setDrawLooper(0); 1042 return; 1043 } 1044 1045 double width = size.width(); 1046 double height = size.height(); 1047 double blur = blurFloat; 1048 1049 uint32_t mfFlags = SkBlurMaskFilter::kHighQuality_BlurFlag; 1050 SkXfermode::Mode colorMode = SkXfermode::kSrc_Mode; 1051 1052 if (m_state.shadowsIgnoreTransforms) { 1053 // Currently only the GraphicsContext associated with the 1054 // CanvasRenderingContext for HTMLCanvasElement have shadows ignore 1055 // Transforms. So with this flag set, we know this state is associated 1056 // with a CanvasRenderingContext. 1057 mfFlags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag; 1058 1059 // CSS wants us to ignore the original's alpha, but Canvas wants us to 1060 // modulate with it. Using shadowsIgnoreTransforms to tell us that we're 1061 // in a Canvas, we change the colormode to kDst_Mode, so we don't overwrite 1062 // it with our layer's (default opaque-black) color. 1063 colorMode = SkXfermode::kDst_Mode; 1064 1065 // CG uses natural orientation for Y axis, but the HTML5 canvas spec 1066 // does not. 1067 // So we now flip the height since it was flipped in 1068 // CanvasRenderingContext in order to work with CG. 1069 height = -height; 1070 } 1071 1072 SkColor c; 1073 if (color.isValid()) 1074 c = color.rgb(); 1075 else 1076 c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" apple shadow color. 1077 1078 // TODO(tc): Should we have a max value for the blur? CG clamps at 1000.0 1079 // for perf reasons. 1080 1081 SkLayerDrawLooper* dl = new SkLayerDrawLooper; 1082 SkAutoUnref aur(dl); 1083 1084 // top layer, we just draw unchanged 1085 dl->addLayer(); 1086 1087 // lower layer contains our offset, blur, and colorfilter 1088 SkLayerDrawLooper::LayerInfo info; 1089 1090 info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit; // our blur 1091 info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit; 1092 info.fColorMode = colorMode; 1093 info.fOffset.set(width, height); 1094 info.fPostTranslate = m_state.shadowsIgnoreTransforms; 1095 1096 SkMaskFilter* mf = SkBlurMaskFilter::Create(blur / 2, SkBlurMaskFilter::kNormal_BlurStyle, mfFlags); 1097 1098 SkColorFilter* cf = SkColorFilter::CreateModeFilter(c, SkXfermode::kSrcIn_Mode); 1099 1100 SkPaint* paint = dl->addLayer(info); 1101 SkSafeUnref(paint->setMaskFilter(mf)); 1102 SkSafeUnref(paint->setColorFilter(cf)); 1103 1104 // dl is now built, just install it 1105 platformContext()->setDrawLooper(dl); 1106 } 1107 1108 void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor, ColorSpace colorSpace) 1109 { 1110 if (paintingDisabled()) 1111 return; 1112 1113 platformContext()->setStrokeColor(strokecolor.rgb()); 1114 } 1115 1116 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle stroke) 1117 { 1118 if (paintingDisabled()) 1119 return; 1120 1121 platformContext()->setStrokeStyle(stroke); 1122 } 1123 1124 void GraphicsContext::setPlatformStrokeThickness(float thickness) 1125 { 1126 if (paintingDisabled()) 1127 return; 1128 1129 platformContext()->setStrokeThickness(thickness); 1130 } 1131 1132 void GraphicsContext::setPlatformStrokeGradient(Gradient* gradient) 1133 { 1134 if (paintingDisabled()) 1135 return; 1136 1137 platformContext()->setStrokeShader(gradient->platformGradient()); 1138 } 1139 1140 void GraphicsContext::setPlatformStrokePattern(Pattern* pattern) 1141 { 1142 if (paintingDisabled()) 1143 return; 1144 1145 platformContext()->setStrokeShader(pattern->platformPattern(getCTM())); 1146 } 1147 1148 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode) 1149 { 1150 if (paintingDisabled()) 1151 return; 1152 1153 platformContext()->setTextDrawingMode(mode); 1154 } 1155 1156 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) 1157 { 1158 } 1159 1160 void GraphicsContext::setPlatformShouldAntialias(bool enable) 1161 { 1162 if (paintingDisabled()) 1163 return; 1164 1165 platformContext()->setUseAntialiasing(enable); 1166 } 1167 1168 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) 1169 { 1170 if (paintingDisabled()) 1171 return; 1172 1173 platformContext()->prepareForSoftwareDraw(); 1174 1175 SkPaint paint; 1176 SkRect oval = r; 1177 if (strokeStyle() == NoStroke) { 1178 // Stroke using the fill color. 1179 // TODO(brettw) is this really correct? It seems unreasonable. 1180 platformContext()->setupPaintForFilling(&paint); 1181 paint.setStyle(SkPaint::kStroke_Style); 1182 paint.setStrokeWidth(WebCoreFloatToSkScalar(strokeThickness())); 1183 } else 1184 platformContext()->setupPaintForStroking(&paint, 0, 0); 1185 1186 // We do this before converting to scalar, so we don't overflow SkFixed. 1187 startAngle = fastMod(startAngle, 360); 1188 angleSpan = fastMod(angleSpan, 360); 1189 1190 SkPath path; 1191 path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan)); 1192 if (!isPathSkiaSafe(getCTM(), path)) 1193 return; 1194 platformContext()->canvas()->drawPath(path, paint); 1195 } 1196 1197 void GraphicsContext::strokePath(const Path& pathToStroke) 1198 { 1199 if (paintingDisabled()) 1200 return; 1201 1202 SkPath path = *pathToStroke.platformPath(); 1203 if (!isPathSkiaSafe(getCTM(), path)) 1204 return; 1205 1206 platformContext()->prepareForSoftwareDraw(); 1207 1208 SkPaint paint; 1209 platformContext()->setupPaintForStroking(&paint, 0, 0); 1210 platformContext()->canvas()->drawPath(path, paint); 1211 } 1212 1213 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) 1214 { 1215 if (paintingDisabled()) 1216 return; 1217 1218 if (!isRectSkiaSafe(getCTM(), rect)) 1219 return; 1220 1221 platformContext()->prepareForSoftwareDraw(); 1222 1223 SkPaint paint; 1224 platformContext()->setupPaintForStroking(&paint, 0, 0); 1225 paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth)); 1226 platformContext()->canvas()->drawRect(rect, paint); 1227 } 1228 1229 void GraphicsContext::rotate(float angleInRadians) 1230 { 1231 if (paintingDisabled()) 1232 return; 1233 1234 if (platformContext()->useGPU()) 1235 platformContext()->gpuCanvas()->rotate(angleInRadians); 1236 1237 platformContext()->canvas()->rotate(WebCoreFloatToSkScalar( 1238 angleInRadians * (180.0f / 3.14159265f))); 1239 } 1240 1241 void GraphicsContext::translate(float w, float h) 1242 { 1243 if (paintingDisabled()) 1244 return; 1245 1246 if (platformContext()->useGPU()) 1247 platformContext()->gpuCanvas()->translate(w, h); 1248 1249 platformContext()->canvas()->translate(WebCoreFloatToSkScalar(w), 1250 WebCoreFloatToSkScalar(h)); 1251 } 1252 1253 void GraphicsContext::syncSoftwareCanvas() 1254 { 1255 platformContext()->syncSoftwareCanvas(); 1256 } 1257 1258 void GraphicsContext::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* framebuffer, const IntSize& size) 1259 { 1260 platformContext()->setSharedGraphicsContext3D(context, framebuffer, size); 1261 } 1262 1263 void GraphicsContext::markDirtyRect(const IntRect& rect) 1264 { 1265 platformContext()->markDirtyRect(rect); 1266 } 1267 1268 } // namespace WebCore 1269