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