1/* 2 * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2013 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 * THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "platform/graphics/GraphicsContext.h" 29 30#include "platform/TraceEvent.h" 31#include "platform/geometry/IntRect.h" 32#include "platform/geometry/RoundedRect.h" 33#include "platform/graphics/BitmapImage.h" 34#include "platform/graphics/DisplayList.h" 35#include "platform/graphics/Gradient.h" 36#include "platform/graphics/ImageBuffer.h" 37#include "platform/text/BidiResolver.h" 38#include "platform/text/TextRunIterator.h" 39#include "platform/weborigin/KURL.h" 40#include "third_party/skia/include/core/SkAnnotation.h" 41#include "third_party/skia/include/core/SkColorFilter.h" 42#include "third_party/skia/include/core/SkData.h" 43#include "third_party/skia/include/core/SkDevice.h" 44#include "third_party/skia/include/core/SkPicture.h" 45#include "third_party/skia/include/core/SkRRect.h" 46#include "third_party/skia/include/core/SkRefCnt.h" 47#include "third_party/skia/include/core/SkSurface.h" 48#include "third_party/skia/include/effects/SkBlurMaskFilter.h" 49#include "third_party/skia/include/effects/SkCornerPathEffect.h" 50#include "third_party/skia/include/effects/SkLumaColorFilter.h" 51#include "third_party/skia/include/gpu/GrRenderTarget.h" 52#include "third_party/skia/include/gpu/GrTexture.h" 53#include "wtf/Assertions.h" 54#include "wtf/MathExtras.h" 55 56using namespace std; 57using blink::WebBlendMode; 58 59namespace WebCore { 60 61namespace { 62 63class CompatibleImageBufferSurface : public ImageBufferSurface { 64 WTF_MAKE_NONCOPYABLE(CompatibleImageBufferSurface); WTF_MAKE_FAST_ALLOCATED; 65public: 66 CompatibleImageBufferSurface(PassRefPtr<SkSurface> surface, const IntSize& size, OpacityMode opacityMode) 67 : ImageBufferSurface(size, opacityMode) 68 , m_surface(surface) 69 { 70 } 71 virtual ~CompatibleImageBufferSurface() { } 72 73 virtual SkCanvas* canvas() const OVERRIDE { return m_surface ? m_surface->getCanvas() : 0; } 74 virtual bool isValid() const OVERRIDE { return m_surface; } 75 virtual bool isAccelerated() const OVERRIDE { return isValid() && m_surface->getCanvas()->getTopDevice()->accessRenderTarget(); } 76 virtual Platform3DObject getBackingTexture() const OVERRIDE 77 { 78 ASSERT(isAccelerated()); 79 GrRenderTarget* renderTarget = m_surface->getCanvas()->getTopDevice()->accessRenderTarget(); 80 if (renderTarget) { 81 return renderTarget->asTexture()->getTextureHandle(); 82 } 83 return 0; 84 }; 85 86private: 87 RefPtr<SkSurface> m_surface; 88}; 89 90} // unnamed namespace 91 92struct GraphicsContext::CanvasSaveState { 93 CanvasSaveState(bool pendingSave, int count) 94 : m_pendingSave(pendingSave), m_restoreCount(count) { } 95 96 bool m_pendingSave; 97 int m_restoreCount; 98}; 99 100struct GraphicsContext::RecordingState { 101 RecordingState(SkCanvas* currentCanvas, const SkMatrix& currentMatrix, PassRefPtr<DisplayList> displayList) 102 : m_savedCanvas(currentCanvas) 103 , m_displayList(displayList) 104 , m_savedMatrix(currentMatrix) 105 { 106 } 107 108 SkCanvas* m_savedCanvas; 109 RefPtr<DisplayList> m_displayList; 110 const SkMatrix m_savedMatrix; 111}; 112 113GraphicsContext::GraphicsContext(SkCanvas* canvas, DisabledMode disableContextOrPainting) 114 : m_canvas(canvas) 115 , m_paintStateStack() 116 , m_paintStateIndex(0) 117 , m_pendingCanvasSave(false) 118 , m_annotationMode(0) 119#if ASSERT_ENABLED 120 , m_annotationCount(0) 121 , m_layerCount(0) 122 , m_disableDestructionChecks(false) 123#endif 124 , m_disabledState(disableContextOrPainting) 125 , m_trackOpaqueRegion(false) 126 , m_trackTextRegion(false) 127 , m_useHighResMarker(false) 128 , m_updatingControlTints(false) 129 , m_accelerated(false) 130 , m_isCertainlyOpaque(true) 131 , m_printing(false) 132 , m_antialiasHairlineImages(false) 133{ 134 if (!canvas) 135 m_disabledState |= PaintingDisabled; 136 137 // FIXME: Do some tests to determine how many states are typically used, and allocate 138 // several here. 139 m_paintStateStack.append(GraphicsContextState::create()); 140 m_paintState = m_paintStateStack.last().get(); 141} 142 143GraphicsContext::~GraphicsContext() 144{ 145#if ASSERT_ENABLED 146 if (!m_disableDestructionChecks) { 147 ASSERT(!m_paintStateIndex); 148 ASSERT(!m_paintState->saveCount()); 149 ASSERT(!m_annotationCount); 150 ASSERT(!m_layerCount); 151 ASSERT(m_recordingStateStack.isEmpty()); 152 ASSERT(m_canvasStateStack.isEmpty()); 153 } 154#endif 155} 156 157void GraphicsContext::save() 158{ 159 if (contextDisabled()) 160 return; 161 162 m_paintState->incrementSaveCount(); 163 164 m_canvasStateStack.append(CanvasSaveState(m_pendingCanvasSave, m_canvas->getSaveCount())); 165 m_pendingCanvasSave = true; 166} 167 168void GraphicsContext::restore() 169{ 170 if (contextDisabled()) 171 return; 172 173 if (!m_paintStateIndex && !m_paintState->saveCount()) { 174 WTF_LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty"); 175 return; 176 } 177 178 if (m_paintState->saveCount()) { 179 m_paintState->decrementSaveCount(); 180 } else { 181 m_paintStateIndex--; 182 m_paintState = m_paintStateStack[m_paintStateIndex].get(); 183 } 184 185 CanvasSaveState savedState = m_canvasStateStack.last(); 186 m_canvasStateStack.removeLast(); 187 m_pendingCanvasSave = savedState.m_pendingSave; 188 m_canvas->restoreToCount(savedState.m_restoreCount); 189} 190 191void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint) 192{ 193 if (contextDisabled()) 194 return; 195 196 realizeCanvasSave(); 197 198 m_canvas->saveLayer(bounds, paint); 199 if (m_trackOpaqueRegion) 200 m_opaqueRegion.pushCanvasLayer(paint); 201} 202 203void GraphicsContext::restoreLayer() 204{ 205 if (contextDisabled()) 206 return; 207 208 m_canvas->restore(); 209 if (m_trackOpaqueRegion) 210 m_opaqueRegion.popCanvasLayer(this); 211} 212 213void GraphicsContext::beginAnnotation(const char* rendererName, const char* paintPhase, 214 const String& elementId, const String& elementClass, const String& elementTag) 215{ 216 if (contextDisabled()) 217 return; 218 219 canvas()->beginCommentGroup("GraphicsContextAnnotation"); 220 221 GraphicsContextAnnotation annotation(rendererName, paintPhase, elementId, elementClass, elementTag); 222 AnnotationList annotations; 223 annotation.asAnnotationList(annotations); 224 225 AnnotationList::const_iterator end = annotations.end(); 226 for (AnnotationList::const_iterator it = annotations.begin(); it != end; ++it) 227 canvas()->addComment(it->first, it->second.ascii().data()); 228 229#if ASSERT_ENABLED 230 ++m_annotationCount; 231#endif 232} 233 234void GraphicsContext::endAnnotation() 235{ 236 if (contextDisabled()) 237 return; 238 239 canvas()->endCommentGroup(); 240 241 ASSERT(m_annotationCount > 0); 242#if ASSERT_ENABLED 243 --m_annotationCount; 244#endif 245} 246 247void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern) 248{ 249 if (contextDisabled()) 250 return; 251 252 ASSERT(pattern); 253 if (!pattern) { 254 setStrokeColor(Color::black); 255 return; 256 } 257 mutableState()->setStrokePattern(pattern); 258} 259 260void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient) 261{ 262 if (contextDisabled()) 263 return; 264 265 ASSERT(gradient); 266 if (!gradient) { 267 setStrokeColor(Color::black); 268 return; 269 } 270 mutableState()->setStrokeGradient(gradient); 271} 272 273void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern) 274{ 275 if (contextDisabled()) 276 return; 277 278 ASSERT(pattern); 279 if (!pattern) { 280 setFillColor(Color::black); 281 return; 282 } 283 284 mutableState()->setFillPattern(pattern); 285} 286 287void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) 288{ 289 if (contextDisabled()) 290 return; 291 292 ASSERT(gradient); 293 if (!gradient) { 294 setFillColor(Color::black); 295 return; 296 } 297 298 mutableState()->setFillGradient(gradient); 299} 300 301void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, 302 DrawLooperBuilder::ShadowTransformMode shadowTransformMode, 303 DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode) 304{ 305 if (contextDisabled()) 306 return; 307 308 if (!color.alpha() || (!offset.width() && !offset.height() && !blur)) { 309 clearShadow(); 310 return; 311 } 312 313 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create(); 314 drawLooperBuilder->addShadow(offset, blur, color, shadowTransformMode, shadowAlphaMode); 315 drawLooperBuilder->addUnmodifiedContent(); 316 setDrawLooper(drawLooperBuilder.release()); 317} 318 319void GraphicsContext::setDrawLooper(PassOwnPtr<DrawLooperBuilder> drawLooperBuilder) 320{ 321 if (contextDisabled()) 322 return; 323 324 mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper()); 325} 326 327void GraphicsContext::clearDrawLooper() 328{ 329 if (contextDisabled()) 330 return; 331 332 mutableState()->clearDrawLooper(); 333} 334 335bool GraphicsContext::hasShadow() const 336{ 337 return !!immutableState()->drawLooper(); 338} 339 340bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const 341{ 342 if (contextDisabled()) 343 return false; 344 SkIRect skIBounds; 345 if (!m_canvas->getClipDeviceBounds(&skIBounds)) 346 return false; 347 SkRect skBounds = SkRect::Make(skIBounds); 348 *bounds = FloatRect(skBounds); 349 return true; 350} 351 352SkMatrix GraphicsContext::getTotalMatrix() const 353{ 354 if (contextDisabled()) 355 return SkMatrix::I(); 356 357 if (!isRecording()) 358 return m_canvas->getTotalMatrix(); 359 360 const RecordingState& recordingState = m_recordingStateStack.last(); 361 SkMatrix totalMatrix = recordingState.m_savedMatrix; 362 totalMatrix.preConcat(m_canvas->getTotalMatrix()); 363 364 return totalMatrix; 365} 366 367void GraphicsContext::adjustTextRenderMode(SkPaint* paint) 368{ 369 if (contextDisabled()) 370 return; 371 372 if (!paint->isLCDRenderText()) 373 return; 374 375 paint->setLCDRenderText(couldUseLCDRenderedText()); 376} 377 378bool GraphicsContext::couldUseLCDRenderedText() 379{ 380 // Our layers only have a single alpha channel. This means that subpixel 381 // rendered text cannot be composited correctly when the layer is 382 // collapsed. Therefore, subpixel text is contextDisabled when we are drawing 383 // onto a layer. 384 if (contextDisabled() || m_canvas->isDrawingToLayer() || !isCertainlyOpaque()) 385 return false; 386 387 return shouldSmoothFonts(); 388} 389 390void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode) 391{ 392 if (contextDisabled()) 393 return; 394 mutableState()->setCompositeOperation(compositeOperation, blendMode); 395} 396 397SkColorFilter* GraphicsContext::colorFilter() 398{ 399 return immutableState()->colorFilter(); 400} 401 402void GraphicsContext::setColorFilter(ColorFilter colorFilter) 403{ 404 GraphicsContextState* stateToSet = mutableState(); 405 406 // We only support one active color filter at the moment. If (when) this becomes a problem, 407 // we should switch to using color filter chains (Skia work in progress). 408 ASSERT(!stateToSet->colorFilter()); 409 stateToSet->setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter)); 410} 411 412bool GraphicsContext::readPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y) 413{ 414 if (contextDisabled()) 415 return false; 416 417 return m_canvas->readPixels(info, pixels, rowBytes, x, y); 418} 419 420void GraphicsContext::setMatrix(const SkMatrix& matrix) 421{ 422 if (contextDisabled()) 423 return; 424 425 realizeCanvasSave(); 426 427 m_canvas->setMatrix(matrix); 428} 429 430void GraphicsContext::concat(const SkMatrix& matrix) 431{ 432 if (contextDisabled()) 433 return; 434 435 if (matrix.isIdentity()) 436 return; 437 438 realizeCanvasSave(); 439 440 m_canvas->concat(matrix); 441} 442 443void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bounds) 444{ 445 beginLayer(opacity, immutableState()->compositeOperator(), bounds); 446} 447 448void GraphicsContext::beginLayer(float opacity, CompositeOperator op, const FloatRect* bounds, ColorFilter colorFilter, ImageFilter* imageFilter) 449{ 450 if (contextDisabled()) 451 return; 452 453 SkPaint layerPaint; 454 layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255)); 455 layerPaint.setXfermode(WebCoreCompositeToSkiaComposite(op, m_paintState->blendMode()).get()); 456 layerPaint.setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter).get()); 457 layerPaint.setImageFilter(imageFilter); 458 459 if (bounds) { 460 SkRect skBounds = WebCoreFloatRectToSKRect(*bounds); 461 saveLayer(&skBounds, &layerPaint); 462 } else { 463 saveLayer(0, &layerPaint); 464 } 465 466#if ASSERT_ENABLED 467 ++m_layerCount; 468#endif 469} 470 471void GraphicsContext::endLayer() 472{ 473 if (contextDisabled()) 474 return; 475 476 restoreLayer(); 477 478 ASSERT(m_layerCount > 0); 479#if ASSERT_ENABLED 480 --m_layerCount; 481#endif 482} 483 484void GraphicsContext::beginRecording(const FloatRect& bounds) 485{ 486 RefPtr<DisplayList> displayList = adoptRef(new DisplayList(bounds)); 487 488 SkCanvas* savedCanvas = m_canvas; 489 SkMatrix savedMatrix = getTotalMatrix(); 490 491 if (!contextDisabled()) { 492 IntRect recordingRect = enclosingIntRect(bounds); 493 m_canvas = displayList->beginRecording(recordingRect.size()); 494 495 // We want the bounds offset mapped to (0, 0), such that the display list content 496 // is fully contained within the SkPictureRecord's bounds. 497 if (!toFloatSize(bounds.location()).isZero()) { 498 m_canvas->translate(-bounds.x(), -bounds.y()); 499 // To avoid applying the offset repeatedly in getTotalMatrix(), we pre-apply it here. 500 savedMatrix.preTranslate(bounds.x(), bounds.y()); 501 } 502 } 503 504 m_recordingStateStack.append(RecordingState(savedCanvas, savedMatrix, displayList)); 505} 506 507PassRefPtr<DisplayList> GraphicsContext::endRecording() 508{ 509 ASSERT(!m_recordingStateStack.isEmpty()); 510 511 RecordingState recording = m_recordingStateStack.last(); 512 if (!contextDisabled()) { 513 ASSERT(recording.m_displayList->isRecording()); 514 recording.m_displayList->endRecording(); 515 } 516 517 m_recordingStateStack.removeLast(); 518 m_canvas = recording.m_savedCanvas; 519 520 return recording.m_displayList.release(); 521} 522 523bool GraphicsContext::isRecording() const 524{ 525 return !m_recordingStateStack.isEmpty(); 526} 527 528void GraphicsContext::drawDisplayList(DisplayList* displayList) 529{ 530 ASSERT(displayList); 531 ASSERT(!displayList->isRecording()); 532 533 if (contextDisabled() || displayList->bounds().isEmpty()) 534 return; 535 536 realizeCanvasSave(); 537 538 const FloatRect& bounds = displayList->bounds(); 539 if (bounds.x() || bounds.y()) 540 m_canvas->translate(bounds.x(), bounds.y()); 541 542 m_canvas->drawPicture(displayList->picture()); 543 544 if (bounds.x() || bounds.y()) 545 m_canvas->translate(-bounds.x(), -bounds.y()); 546} 547 548void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias) 549{ 550 if (contextDisabled()) 551 return; 552 553 if (numPoints <= 1) 554 return; 555 556 SkPath path; 557 setPathFromConvexPoints(&path, numPoints, points); 558 559 SkPaint paint(immutableState()->fillPaint()); 560 paint.setAntiAlias(shouldAntialias); 561 drawPath(path, paint); 562 563 if (strokeStyle() != NoStroke) 564 drawPath(path, immutableState()->strokePaint()); 565} 566 567void GraphicsContext::drawFocusRing(const Path& focusRingPath, int width, int offset, const Color& color) 568{ 569 // FIXME: Implement support for offset. 570 if (contextDisabled()) 571 return; 572 573 SkPaint paint; 574 paint.setAntiAlias(true); 575 paint.setStyle(SkPaint::kStroke_Style); 576 paint.setColor(color.rgb()); 577 578 drawOuterPath(focusRingPath.skPath(), paint, width); 579 drawInnerPath(focusRingPath.skPath(), paint, width); 580} 581 582void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) 583{ 584 if (contextDisabled()) 585 return; 586 587 unsigned rectCount = rects.size(); 588 if (!rectCount) 589 return; 590 591 SkRegion focusRingRegion; 592 const int focusRingOutset = getFocusRingOutset(offset); 593 for (unsigned i = 0; i < rectCount; i++) { 594 SkIRect r = rects[i]; 595 r.inset(-focusRingOutset, -focusRingOutset); 596 focusRingRegion.op(r, SkRegion::kUnion_Op); 597 } 598 599 SkPath path; 600 SkPaint paint; 601 paint.setAntiAlias(true); 602 paint.setStyle(SkPaint::kStroke_Style); 603 604 paint.setColor(color.rgb()); 605 focusRingRegion.getBoundaryPath(&path); 606 drawOuterPath(path, paint, width); 607 drawInnerPath(path, paint, width); 608} 609 610static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset) 611{ 612 IntRect bounds(holeRect); 613 614 bounds.inflate(shadowBlur); 615 616 if (shadowSpread < 0) 617 bounds.inflate(-shadowSpread); 618 619 IntRect offsetBounds = bounds; 620 offsetBounds.move(-shadowOffset); 621 return unionRect(bounds, offsetBounds); 622} 623 624void GraphicsContext::drawInnerShadow(const RoundedRect& rect, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges) 625{ 626 if (contextDisabled()) 627 return; 628 629 IntRect holeRect(rect.rect()); 630 holeRect.inflate(-shadowSpread); 631 632 if (holeRect.isEmpty()) { 633 if (rect.isRounded()) 634 fillRoundedRect(rect, shadowColor); 635 else 636 fillRect(rect.rect(), shadowColor); 637 return; 638 } 639 640 if (clippedEdges & LeftEdge) { 641 holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0); 642 holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur); 643 } 644 if (clippedEdges & TopEdge) { 645 holeRect.move(0, -max(shadowOffset.height(), 0) - shadowBlur); 646 holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + shadowBlur); 647 } 648 if (clippedEdges & RightEdge) 649 holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur); 650 if (clippedEdges & BottomEdge) 651 holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + shadowBlur); 652 653 Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255); 654 655 IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, shadowSpread, shadowOffset); 656 RoundedRect roundedHole(holeRect, rect.radii()); 657 658 save(); 659 if (rect.isRounded()) { 660 Path path; 661 path.addRoundedRect(rect); 662 clipPath(path); 663 roundedHole.shrinkRadii(shadowSpread); 664 } else { 665 clip(rect.rect()); 666 } 667 668 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create(); 669 drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor, 670 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha); 671 setDrawLooper(drawLooperBuilder.release()); 672 fillRectWithRoundedHole(outerRect, roundedHole, fillColor); 673 restore(); 674 clearDrawLooper(); 675} 676 677void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) 678{ 679 if (contextDisabled()) 680 return; 681 682 StrokeStyle penStyle = strokeStyle(); 683 if (penStyle == NoStroke) 684 return; 685 686 FloatPoint p1 = point1; 687 FloatPoint p2 = point2; 688 bool isVerticalLine = (p1.x() == p2.x()); 689 int width = roundf(strokeThickness()); 690 691 // We know these are vertical or horizontal lines, so the length will just 692 // be the sum of the displacement component vectors give or take 1 - 693 // probably worth the speed up of no square root, which also won't be exact. 694 FloatSize disp = p2 - p1; 695 int length = SkScalarRoundToInt(disp.width() + disp.height()); 696 SkPaint paint(immutableState()->strokePaint(length)); 697 698 if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { 699 // Do a rect fill of our endpoints. This ensures we always have the 700 // appearance of being a border. We then draw the actual dotted/dashed line. 701 SkRect r1, r2; 702 r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width); 703 r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width); 704 705 if (isVerticalLine) { 706 r1.offset(-width / 2, 0); 707 r2.offset(-width / 2, -width); 708 } else { 709 r1.offset(0, -width / 2); 710 r2.offset(-width, -width / 2); 711 } 712 SkPaint fillPaint; 713 fillPaint.setColor(paint.getColor()); 714 drawRect(r1, fillPaint); 715 drawRect(r2, fillPaint); 716 } 717 718 adjustLineToPixelBoundaries(p1, p2, width, penStyle); 719 SkPoint pts[2] = { p1.data(), p2.data() }; 720 721 m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint); 722 723 if (m_trackOpaqueRegion) 724 m_opaqueRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, paint); 725} 726 727void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style) 728{ 729 if (contextDisabled()) 730 return; 731 732 int deviceScaleFactor = m_useHighResMarker ? 2 : 1; 733 734 // Create the pattern we'll use to draw the underline. 735 int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0; 736 static SkBitmap* misspellBitmap1x[2] = { 0, 0 }; 737 static SkBitmap* misspellBitmap2x[2] = { 0, 0 }; 738 SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x; 739 if (!misspellBitmap[index]) { 740#if OS(MACOSX) 741 // Match the artwork used by the Mac. 742 const int rowPixels = 4 * deviceScaleFactor; 743 const int colPixels = 3 * deviceScaleFactor; 744 SkBitmap bitmap; 745 if (!bitmap.allocN32Pixels(rowPixels, colPixels)) 746 return; 747 748 bitmap.eraseARGB(0, 0, 0, 0); 749 const uint32_t transparentColor = 0x00000000; 750 751 if (deviceScaleFactor == 1) { 752 const uint32_t colors[2][6] = { 753 { 0x2a2a0600, 0x57571000, 0xa8a81b00, 0xbfbf1f00, 0x70701200, 0xe0e02400 }, 754 { 0x2a0f0f0f, 0x571e1e1e, 0xa83d3d3d, 0xbf454545, 0x70282828, 0xe0515151 } 755 }; 756 757 // Pattern: a b a a b a 758 // c d c c d c 759 // e f e e f e 760 for (int x = 0; x < colPixels; ++x) { 761 uint32_t* row = bitmap.getAddr32(0, x); 762 row[0] = colors[index][x * 2]; 763 row[1] = colors[index][x * 2 + 1]; 764 row[2] = colors[index][x * 2]; 765 row[3] = transparentColor; 766 } 767 } else if (deviceScaleFactor == 2) { 768 const uint32_t colors[2][18] = { 769 { 0x0a090101, 0x33320806, 0x55540f0a, 0x37360906, 0x6e6c120c, 0x6e6c120c, 0x7674140d, 0x8d8b1810, 0x8d8b1810, 770 0x96941a11, 0xb3b01f15, 0xb3b01f15, 0x6d6b130c, 0xd9d62619, 0xd9d62619, 0x19180402, 0x7c7a150e, 0xcecb2418 }, 771 { 0x0a020202, 0x33141414, 0x55232323, 0x37161616, 0x6e2e2e2e, 0x6e2e2e2e, 0x76313131, 0x8d3a3a3a, 0x8d3a3a3a, 772 0x963e3e3e, 0xb34b4b4b, 0xb34b4b4b, 0x6d2d2d2d, 0xd95b5b5b, 0xd95b5b5b, 0x19090909, 0x7c343434, 0xce575757 } 773 }; 774 775 // Pattern: a b c c b a 776 // d e f f e d 777 // g h j j h g 778 // k l m m l k 779 // n o p p o n 780 // q r s s r q 781 for (int x = 0; x < colPixels; ++x) { 782 uint32_t* row = bitmap.getAddr32(0, x); 783 row[0] = colors[index][x * 3]; 784 row[1] = colors[index][x * 3 + 1]; 785 row[2] = colors[index][x * 3 + 2]; 786 row[3] = colors[index][x * 3 + 2]; 787 row[4] = colors[index][x * 3 + 1]; 788 row[5] = colors[index][x * 3]; 789 row[6] = transparentColor; 790 row[7] = transparentColor; 791 } 792 } else 793 ASSERT_NOT_REACHED(); 794 795 misspellBitmap[index] = new SkBitmap(bitmap); 796#else 797 // We use a 2-pixel-high misspelling indicator because that seems to be 798 // what WebKit is designed for, and how much room there is in a typical 799 // page for it. 800 const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below. 801 const int colPixels = 2 * deviceScaleFactor; 802 SkBitmap bitmap; 803 if (!bitmap.allocN32Pixels(rowPixels, colPixels)) 804 return; 805 806 bitmap.eraseARGB(0, 0, 0, 0); 807 if (deviceScaleFactor == 1) 808 draw1xMarker(&bitmap, index); 809 else if (deviceScaleFactor == 2) 810 draw2xMarker(&bitmap, index); 811 else 812 ASSERT_NOT_REACHED(); 813 814 misspellBitmap[index] = new SkBitmap(bitmap); 815#endif 816 } 817 818#if OS(MACOSX) 819 SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor; 820 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor; 821 822 // Make sure to draw only complete dots. 823 int rowPixels = misspellBitmap[index]->width(); 824 float widthMod = fmodf(width * deviceScaleFactor, rowPixels); 825 if (rowPixels - widthMod > deviceScaleFactor) 826 width -= widthMod / deviceScaleFactor; 827#else 828 SkScalar originX = WebCoreFloatToSkScalar(pt.x()); 829 830 // Offset it vertically by 1 so that there's some space under the text. 831 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1; 832 originX *= deviceScaleFactor; 833 originY *= deviceScaleFactor; 834#endif 835 836 SkMatrix localMatrix; 837 localMatrix.setTranslate(originX, originY); 838 RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader( 839 *misspellBitmap[index], SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); 840 841 SkPaint paint; 842 paint.setShader(shader.get()); 843 844 SkRect rect; 845 rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height())); 846 847 if (deviceScaleFactor == 2) { 848 save(); 849 scale(0.5, 0.5); 850 } 851 drawRect(rect, paint); 852 if (deviceScaleFactor == 2) 853 restore(); 854} 855 856void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool printing) 857{ 858 if (contextDisabled()) 859 return; 860 861 if (width <= 0) 862 return; 863 864 SkPaint paint; 865 switch (strokeStyle()) { 866 case NoStroke: 867 case SolidStroke: 868 case DoubleStroke: 869 case WavyStroke: { 870 int thickness = SkMax32(static_cast<int>(strokeThickness()), 1); 871 SkRect r; 872 r.fLeft = WebCoreFloatToSkScalar(pt.x()); 873 // Avoid anti-aliasing lines. Currently, these are always horizontal. 874 // Round to nearest pixel to match text and other content. 875 r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f)); 876 r.fRight = r.fLeft + WebCoreFloatToSkScalar(width); 877 r.fBottom = r.fTop + SkIntToScalar(thickness); 878 paint = immutableState()->fillPaint(); 879 // Text lines are drawn using the stroke color. 880 paint.setColor(effectiveStrokeColor()); 881 drawRect(r, paint); 882 return; 883 } 884 case DottedStroke: 885 case DashedStroke: { 886 int y = floorf(pt.y() + std::max<float>(strokeThickness() / 2.0f, 0.5f)); 887 drawLine(IntPoint(pt.x(), y), IntPoint(pt.x() + width, y)); 888 return; 889 } 890 } 891 892 ASSERT_NOT_REACHED(); 893} 894 895// Draws a filled rectangle with a stroked border. 896void GraphicsContext::drawRect(const IntRect& rect) 897{ 898 if (contextDisabled()) 899 return; 900 901 ASSERT(!rect.isEmpty()); 902 if (rect.isEmpty()) 903 return; 904 905 SkRect skRect = rect; 906 int fillcolorNotTransparent = immutableState()->fillColor().rgb() & 0xFF000000; 907 if (fillcolorNotTransparent) 908 drawRect(skRect, immutableState()->fillPaint()); 909 910 if (immutableState()->strokeData().style() != NoStroke 911 && immutableState()->strokeData().color().alpha()) { 912 // Stroke a width: 1 inset border 913 SkPaint paint(immutableState()->fillPaint()); 914 paint.setColor(effectiveStrokeColor()); 915 paint.setStyle(SkPaint::kStroke_Style); 916 paint.setStrokeWidth(1); 917 918 skRect.inset(0.5f, 0.5f); 919 drawRect(skRect, paint); 920 } 921} 922 923void GraphicsContext::drawText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point) 924{ 925 if (contextDisabled()) 926 return; 927 928 font.drawText(this, runInfo, point); 929} 930 931void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) 932{ 933 if (contextDisabled()) 934 return; 935 936 font.drawEmphasisMarks(this, runInfo, mark, point); 937} 938 939void GraphicsContext::drawBidiText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction) 940{ 941 if (contextDisabled()) 942 return; 943 944 // sub-run painting is not supported for Bidi text. 945 const TextRun& run = runInfo.run; 946 ASSERT((runInfo.from == 0) && (runInfo.to == run.length())); 947 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; 948 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); 949 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); 950 951 // FIXME: This ownership should be reversed. We should pass BidiRunList 952 // to BidiResolver in createBidiRunsForLine. 953 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); 954 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); 955 if (!bidiRuns.runCount()) 956 return; 957 958 FloatPoint currPoint = point; 959 BidiCharacterRun* bidiRun = bidiRuns.firstRun(); 960 while (bidiRun) { 961 TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); 962 bool isRTL = bidiRun->level() % 2; 963 subrun.setDirection(isRTL ? RTL : LTR); 964 subrun.setDirectionalOverride(bidiRun->dirOverride(false)); 965 966 TextRunPaintInfo subrunInfo(subrun); 967 subrunInfo.bounds = runInfo.bounds; 968 font.drawText(this, subrunInfo, currPoint, customFontNotReadyAction); 969 970 bidiRun = bidiRun->next(); 971 // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. 972 if (bidiRun) 973 currPoint.move(font.width(subrun), 0); 974 } 975 976 bidiRuns.deleteRuns(); 977} 978 979void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, int from, int to) 980{ 981 if (contextDisabled()) 982 return; 983 984 fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor); 985} 986 987void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation) 988{ 989 if (!image) 990 return; 991 drawImage(image, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation); 992} 993 994void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation) 995{ 996 if (!image) 997 return; 998 drawImage(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation); 999} 1000 1001void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation) 1002{ 1003 drawImage(image, dest, src, op, blink::WebBlendModeNormal, shouldRespectImageOrientation); 1004} 1005 1006void GraphicsContext::drawImage(Image* image, const FloatRect& dest) 1007{ 1008 if (!image) 1009 return; 1010 drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size()))); 1011} 1012 1013void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation) 1014{ 1015 if (contextDisabled() || !image) 1016 return; 1017 image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation); 1018} 1019 1020void GraphicsContext::drawTiledImage(Image* image, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing) 1021{ 1022 if (contextDisabled() || !image) 1023 return; 1024 image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repeatSpacing); 1025} 1026 1027void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect, 1028 const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op) 1029{ 1030 if (contextDisabled() || !image) 1031 return; 1032 1033 if (hRule == Image::StretchTile && vRule == Image::StretchTile) { 1034 // Just do a scale. 1035 drawImage(image, dest, srcRect, op); 1036 return; 1037 } 1038 1039 image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op); 1040} 1041 1042void GraphicsContext::drawImageBuffer(ImageBuffer* image, const FloatRect& dest, 1043 const FloatRect* src, CompositeOperator op) 1044{ 1045 if (contextDisabled() || !image) 1046 return; 1047 1048 image->draw(this, dest, src, op); 1049} 1050 1051void GraphicsContext::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y) 1052{ 1053 if (contextDisabled()) 1054 return; 1055 1056 m_canvas->writePixels(info, pixels, rowBytes, x, y); 1057 1058 if (m_trackOpaqueRegion) { 1059 SkRect rect = SkRect::MakeXYWH(x, y, info.width(), info.height()); 1060 SkPaint paint; 1061 1062 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 1063 if (kOpaque_SkAlphaType != info.alphaType()) 1064 paint.setAlpha(0x80); // signal to m_opaqueRegion that we are not fully opaque 1065 1066 m_opaqueRegion.didDrawRect(this, rect, paint, 0); 1067 // more efficient would be to call markRectAsOpaque or MarkRectAsNonOpaque directly, 1068 // rather than cons-ing up a paint with an xfermode and alpha 1069 } 1070} 1071 1072void GraphicsContext::writePixels(const SkBitmap& bitmap, int x, int y) 1073{ 1074 if (contextDisabled()) 1075 return; 1076 1077 if (!bitmap.getTexture()) { 1078 SkAutoLockPixels alp(bitmap); 1079 if (bitmap.getPixels()) 1080 writePixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), x, y); 1081 } 1082} 1083 1084void GraphicsContext::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint) 1085{ 1086 if (contextDisabled()) 1087 return; 1088 1089 m_canvas->drawBitmap(bitmap, left, top, paint); 1090 1091 if (m_trackOpaqueRegion) { 1092 SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height()); 1093 m_opaqueRegion.didDrawRect(this, rect, *paint, &bitmap); 1094 } 1095} 1096 1097void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, 1098 const SkRect& dst, const SkPaint* paint) 1099{ 1100 if (contextDisabled()) 1101 return; 1102 1103 SkCanvas::DrawBitmapRectFlags flags = 1104 immutableState()->shouldClampToSourceRect() ? SkCanvas::kNone_DrawBitmapRectFlag : SkCanvas::kBleed_DrawBitmapRectFlag; 1105 1106 m_canvas->drawBitmapRectToRect(bitmap, src, dst, paint, flags); 1107 1108 if (m_trackOpaqueRegion) 1109 m_opaqueRegion.didDrawRect(this, dst, *paint, &bitmap); 1110} 1111 1112void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint) 1113{ 1114 if (contextDisabled()) 1115 return; 1116 1117 m_canvas->drawOval(oval, paint); 1118 1119 if (m_trackOpaqueRegion) 1120 m_opaqueRegion.didDrawBounded(this, oval, paint); 1121} 1122 1123void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint) 1124{ 1125 if (contextDisabled()) 1126 return; 1127 1128 m_canvas->drawPath(path, paint); 1129 1130 if (m_trackOpaqueRegion) 1131 m_opaqueRegion.didDrawPath(this, path, paint); 1132} 1133 1134void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint) 1135{ 1136 if (contextDisabled()) 1137 return; 1138 1139 m_canvas->drawRect(rect, paint); 1140 1141 if (m_trackOpaqueRegion) 1142 m_opaqueRegion.didDrawRect(this, rect, paint, 0); 1143} 1144 1145void GraphicsContext::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap) 1146{ 1147 if (contextDisabled()) 1148 return; 1149 1150 if (m_trackOpaqueRegion) 1151 m_opaqueRegion.didDrawRect(this, rect, paint, bitmap); 1152} 1153 1154void GraphicsContext::drawPosText(const void* text, size_t byteLength, 1155 const SkPoint pos[], const SkRect& textRect, const SkPaint& paint) 1156{ 1157 if (contextDisabled()) 1158 return; 1159 1160 m_canvas->drawPosText(text, byteLength, pos, paint); 1161 didDrawTextInRect(textRect); 1162 1163 // FIXME: compute bounds for positioned text. 1164 if (m_trackOpaqueRegion) 1165 m_opaqueRegion.didDrawUnbounded(this, paint, OpaqueRegionSkia::FillOrStroke); 1166} 1167 1168void GraphicsContext::fillPath(const Path& pathToFill) 1169{ 1170 if (contextDisabled() || pathToFill.isEmpty()) 1171 return; 1172 1173 // Use const_cast and temporarily modify the fill type instead of copying the path. 1174 SkPath& path = const_cast<SkPath&>(pathToFill.skPath()); 1175 SkPath::FillType previousFillType = path.getFillType(); 1176 1177 SkPath::FillType temporaryFillType = 1178 immutableState()->fillRule() == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType; 1179 path.setFillType(temporaryFillType); 1180 1181 drawPath(path, immutableState()->fillPaint()); 1182 1183 path.setFillType(previousFillType); 1184} 1185 1186void GraphicsContext::fillRect(const FloatRect& rect) 1187{ 1188 if (contextDisabled()) 1189 return; 1190 1191 SkRect r = rect; 1192 1193 drawRect(r, immutableState()->fillPaint()); 1194} 1195 1196void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) 1197{ 1198 if (contextDisabled()) 1199 return; 1200 1201 SkRect r = rect; 1202 SkPaint paint = immutableState()->fillPaint(); 1203 paint.setColor(color.rgb()); 1204 drawRect(r, paint); 1205} 1206 1207void GraphicsContext::fillBetweenRoundedRects(const IntRect& outer, const IntSize& outerTopLeft, const IntSize& outerTopRight, const IntSize& outerBottomLeft, const IntSize& outerBottomRight, 1208 const IntRect& inner, const IntSize& innerTopLeft, const IntSize& innerTopRight, const IntSize& innerBottomLeft, const IntSize& innerBottomRight, const Color& color) { 1209 if (contextDisabled()) 1210 return; 1211 1212 SkVector outerRadii[4]; 1213 SkVector innerRadii[4]; 1214 setRadii(outerRadii, outerTopLeft, outerTopRight, outerBottomRight, outerBottomLeft); 1215 setRadii(innerRadii, innerTopLeft, innerTopRight, innerBottomRight, innerBottomLeft); 1216 1217 SkRRect rrOuter; 1218 SkRRect rrInner; 1219 rrOuter.setRectRadii(outer, outerRadii); 1220 rrInner.setRectRadii(inner, innerRadii); 1221 1222 SkPaint paint(immutableState()->fillPaint()); 1223 paint.setColor(color.rgb()); 1224 1225 m_canvas->drawDRRect(rrOuter, rrInner, paint); 1226 1227 if (m_trackOpaqueRegion) 1228 m_opaqueRegion.didDrawBounded(this, rrOuter.getBounds(), paint); 1229} 1230 1231void GraphicsContext::fillBetweenRoundedRects(const RoundedRect& outer, const RoundedRect& inner, const Color& color) 1232{ 1233 fillBetweenRoundedRects(outer.rect(), outer.radii().topLeft(), outer.radii().topRight(), outer.radii().bottomLeft(), outer.radii().bottomRight(), 1234 inner.rect(), inner.radii().topLeft(), inner.radii().topRight(), inner.radii().bottomLeft(), inner.radii().bottomRight(), color); 1235} 1236 1237void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, 1238 const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color) 1239{ 1240 if (contextDisabled()) 1241 return; 1242 1243 if (topLeft.width() + topRight.width() > rect.width() 1244 || bottomLeft.width() + bottomRight.width() > rect.width() 1245 || topLeft.height() + bottomLeft.height() > rect.height() 1246 || topRight.height() + bottomRight.height() > rect.height()) { 1247 // Not all the radii fit, return a rect. This matches the behavior of 1248 // Path::createRoundedRectangle. Without this we attempt to draw a round 1249 // shadow for a square box. 1250 fillRect(rect, color); 1251 return; 1252 } 1253 1254 SkVector radii[4]; 1255 setRadii(radii, topLeft, topRight, bottomRight, bottomLeft); 1256 1257 SkRRect rr; 1258 rr.setRectRadii(rect, radii); 1259 1260 SkPaint paint(immutableState()->fillPaint()); 1261 paint.setColor(color.rgb()); 1262 1263 m_canvas->drawRRect(rr, paint); 1264 1265 if (m_trackOpaqueRegion) 1266 m_opaqueRegion.didDrawBounded(this, rr.getBounds(), paint); 1267} 1268 1269void GraphicsContext::fillEllipse(const FloatRect& ellipse) 1270{ 1271 if (contextDisabled()) 1272 return; 1273 1274 SkRect rect = ellipse; 1275 drawOval(rect, immutableState()->fillPaint()); 1276} 1277 1278void GraphicsContext::strokePath(const Path& pathToStroke) 1279{ 1280 if (contextDisabled() || pathToStroke.isEmpty()) 1281 return; 1282 1283 const SkPath& path = pathToStroke.skPath(); 1284 drawPath(path, immutableState()->strokePaint()); 1285} 1286 1287void GraphicsContext::strokeRect(const FloatRect& rect) 1288{ 1289 strokeRect(rect, strokeThickness()); 1290} 1291 1292void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) 1293{ 1294 if (contextDisabled()) 1295 return; 1296 1297 SkPaint paint(immutableState()->strokePaint()); 1298 paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth)); 1299 // Reset the dash effect to account for the width 1300 immutableState()->strokeData().setupPaintDashPathEffect(&paint, 0); 1301 // strokerect has special rules for CSS when the rect is degenerate: 1302 // if width==0 && height==0, do nothing 1303 // if width==0 || height==0, then just draw line for the other dimension 1304 SkRect r(rect); 1305 bool validW = r.width() > 0; 1306 bool validH = r.height() > 0; 1307 if (validW && validH) { 1308 drawRect(r, paint); 1309 } else if (validW || validH) { 1310 // we are expected to respect the lineJoin, so we can't just call 1311 // drawLine -- we have to create a path that doubles back on itself. 1312 SkPath path; 1313 path.moveTo(r.fLeft, r.fTop); 1314 path.lineTo(r.fRight, r.fBottom); 1315 path.close(); 1316 drawPath(path, paint); 1317 } 1318} 1319 1320void GraphicsContext::strokeEllipse(const FloatRect& ellipse) 1321{ 1322 if (contextDisabled()) 1323 return; 1324 1325 drawOval(ellipse, immutableState()->strokePaint()); 1326} 1327 1328void GraphicsContext::clipRoundedRect(const RoundedRect& rect, SkRegion::Op regionOp) 1329{ 1330 if (contextDisabled()) 1331 return; 1332 1333 if (!rect.isRounded()) { 1334 clipRect(rect.rect(), NotAntiAliased, regionOp); 1335 return; 1336 } 1337 1338 SkVector radii[4]; 1339 RoundedRect::Radii wkRadii = rect.radii(); 1340 setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight(), wkRadii.bottomLeft()); 1341 1342 SkRRect r; 1343 r.setRectRadii(rect.rect(), radii); 1344 1345 clipRRect(r, AntiAliased, regionOp); 1346} 1347 1348void GraphicsContext::clipOut(const Path& pathToClip) 1349{ 1350 if (contextDisabled()) 1351 return; 1352 1353 // Use const_cast and temporarily toggle the inverse fill type instead of copying the path. 1354 SkPath& path = const_cast<SkPath&>(pathToClip.skPath()); 1355 path.toggleInverseFillType(); 1356 clipPath(path, AntiAliased); 1357 path.toggleInverseFillType(); 1358} 1359 1360void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) 1361{ 1362 if (contextDisabled() || pathToClip.isEmpty()) 1363 return; 1364 1365 // Use const_cast and temporarily modify the fill type instead of copying the path. 1366 SkPath& path = const_cast<SkPath&>(pathToClip.skPath()); 1367 SkPath::FillType previousFillType = path.getFillType(); 1368 1369 SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType; 1370 path.setFillType(temporaryFillType); 1371 clipPath(path, AntiAliased); 1372 1373 path.setFillType(previousFillType); 1374} 1375 1376void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) 1377{ 1378 if (contextDisabled()) 1379 return; 1380 1381 if (numPoints <= 1) 1382 return; 1383 1384 SkPath path; 1385 setPathFromConvexPoints(&path, numPoints, points); 1386 clipPath(path, antialiased ? AntiAliased : NotAntiAliased); 1387} 1388 1389void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect) 1390{ 1391 if (contextDisabled()) 1392 return; 1393 1394 clipRoundedRect(rect, SkRegion::kDifference_Op); 1395} 1396 1397void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule) 1398{ 1399 if (contextDisabled()) 1400 return; 1401 1402 // Use const_cast and temporarily modify the fill type instead of copying the path. 1403 SkPath& path = const_cast<SkPath&>(pathToClip.skPath()); 1404 SkPath::FillType previousFillType = path.getFillType(); 1405 1406 SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType; 1407 path.setFillType(temporaryFillType); 1408 clipPath(path); 1409 1410 path.setFillType(previousFillType); 1411} 1412 1413void GraphicsContext::clipRect(const SkRect& rect, AntiAliasingMode aa, SkRegion::Op op) 1414{ 1415 if (contextDisabled()) 1416 return; 1417 1418 realizeCanvasSave(); 1419 1420 m_canvas->clipRect(rect, op, aa == AntiAliased); 1421} 1422 1423void GraphicsContext::clipPath(const SkPath& path, AntiAliasingMode aa, SkRegion::Op op) 1424{ 1425 if (contextDisabled()) 1426 return; 1427 1428 realizeCanvasSave(); 1429 1430 m_canvas->clipPath(path, op, aa == AntiAliased); 1431} 1432 1433void GraphicsContext::clipRRect(const SkRRect& rect, AntiAliasingMode aa, SkRegion::Op op) 1434{ 1435 if (contextDisabled()) 1436 return; 1437 1438 realizeCanvasSave(); 1439 1440 m_canvas->clipRRect(rect, op, aa == AntiAliased); 1441} 1442 1443void GraphicsContext::beginCull(const FloatRect& rect) 1444{ 1445 if (contextDisabled()) 1446 return; 1447 1448 realizeCanvasSave(); 1449 m_canvas->pushCull(rect); 1450} 1451 1452void GraphicsContext::endCull() 1453{ 1454 if (contextDisabled()) 1455 return; 1456 1457 realizeCanvasSave(); 1458 1459 m_canvas->popCull(); 1460} 1461 1462void GraphicsContext::rotate(float angleInRadians) 1463{ 1464 if (contextDisabled()) 1465 return; 1466 1467 realizeCanvasSave(); 1468 1469 m_canvas->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f))); 1470} 1471 1472void GraphicsContext::translate(float x, float y) 1473{ 1474 if (contextDisabled()) 1475 return; 1476 1477 if (!x && !y) 1478 return; 1479 1480 realizeCanvasSave(); 1481 1482 m_canvas->translate(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y)); 1483} 1484 1485void GraphicsContext::scale(float x, float y) 1486{ 1487 if (contextDisabled()) 1488 return; 1489 1490 if (x == 1.0f && y == 1.0f) 1491 return; 1492 1493 realizeCanvasSave(); 1494 1495 m_canvas->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y)); 1496} 1497 1498void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) 1499{ 1500 if (contextDisabled()) 1501 return; 1502 1503 SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data())); 1504 SkAnnotateRectWithURL(m_canvas, destRect, url.get()); 1505} 1506 1507void GraphicsContext::setURLFragmentForRect(const String& destName, const IntRect& rect) 1508{ 1509 if (contextDisabled()) 1510 return; 1511 1512 SkAutoDataUnref skDestName(SkData::NewWithCString(destName.utf8().data())); 1513 SkAnnotateLinkToDestination(m_canvas, rect, skDestName.get()); 1514} 1515 1516void GraphicsContext::addURLTargetAtPoint(const String& name, const IntPoint& pos) 1517{ 1518 if (contextDisabled()) 1519 return; 1520 1521 SkAutoDataUnref nameData(SkData::NewWithCString(name.utf8().data())); 1522 SkAnnotateNamedDestination(m_canvas, SkPoint::Make(pos.x(), pos.y()), nameData); 1523} 1524 1525AffineTransform GraphicsContext::getCTM() const 1526{ 1527 if (contextDisabled()) 1528 return AffineTransform(); 1529 1530 SkMatrix m = getTotalMatrix(); 1531 return AffineTransform(SkScalarToDouble(m.getScaleX()), 1532 SkScalarToDouble(m.getSkewY()), 1533 SkScalarToDouble(m.getSkewX()), 1534 SkScalarToDouble(m.getScaleY()), 1535 SkScalarToDouble(m.getTranslateX()), 1536 SkScalarToDouble(m.getTranslateY())); 1537} 1538 1539void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op) 1540{ 1541 if (contextDisabled()) 1542 return; 1543 1544 CompositeOperator previousOperator = compositeOperation(); 1545 setCompositeOperation(op); 1546 fillRect(rect, color); 1547 setCompositeOperation(previousOperator); 1548} 1549 1550void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color) 1551{ 1552 if (contextDisabled()) 1553 return; 1554 1555 if (rect.isRounded()) 1556 fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color); 1557 else 1558 fillRect(rect.rect(), color); 1559} 1560 1561void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color) 1562{ 1563 if (contextDisabled()) 1564 return; 1565 1566 Path path; 1567 path.addRect(rect); 1568 1569 if (!roundedHoleRect.radii().isZero()) 1570 path.addRoundedRect(roundedHoleRect); 1571 else 1572 path.addRect(roundedHoleRect.rect()); 1573 1574 WindRule oldFillRule = fillRule(); 1575 Color oldFillColor = fillColor(); 1576 1577 setFillRule(RULE_EVENODD); 1578 setFillColor(color); 1579 1580 fillPath(path); 1581 1582 setFillRule(oldFillRule); 1583 setFillColor(oldFillColor); 1584} 1585 1586void GraphicsContext::clearRect(const FloatRect& rect) 1587{ 1588 if (contextDisabled()) 1589 return; 1590 1591 SkRect r = rect; 1592 SkPaint paint(immutableState()->fillPaint()); 1593 paint.setXfermodeMode(SkXfermode::kClear_Mode); 1594 drawRect(r, paint); 1595} 1596 1597void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle) 1598{ 1599 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic 1600 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g., 1601 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave 1602 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5. 1603 if (penStyle == DottedStroke || penStyle == DashedStroke) { 1604 if (p1.x() == p2.x()) { 1605 p1.setY(p1.y() + strokeWidth); 1606 p2.setY(p2.y() - strokeWidth); 1607 } else { 1608 p1.setX(p1.x() + strokeWidth); 1609 p2.setX(p2.x() - strokeWidth); 1610 } 1611 } 1612 1613 if (static_cast<int>(strokeWidth) % 2) { //odd 1614 if (p1.x() == p2.x()) { 1615 // We're a vertical line. Adjust our x. 1616 p1.setX(p1.x() + 0.5f); 1617 p2.setX(p2.x() + 0.5f); 1618 } else { 1619 // We're a horizontal line. Adjust our y. 1620 p1.setY(p1.y() + 0.5f); 1621 p2.setY(p2.y() + 0.5f); 1622 } 1623 } 1624} 1625 1626PassOwnPtr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const IntSize& size, OpacityMode opacityMode) const 1627{ 1628 // Make the buffer larger if the context's transform is scaling it so we need a higher 1629 // resolution than one pixel per unit. Also set up a corresponding scale factor on the 1630 // graphics context. 1631 1632 AffineTransform transform = getCTM(); 1633 IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale()))); 1634 1635 SkAlphaType alphaType = (opacityMode == Opaque) ? kOpaque_SkAlphaType : kPremul_SkAlphaType; 1636 SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), alphaType); 1637 RefPtr<SkSurface> skSurface = adoptRef(m_canvas->newSurface(info)); 1638 if (!skSurface) 1639 return nullptr; 1640 OwnPtr<ImageBufferSurface> surface = adoptPtr(new CompatibleImageBufferSurface(skSurface.release(), scaledSize, opacityMode)); 1641 ASSERT(surface->isValid()); 1642 OwnPtr<ImageBuffer> buffer = adoptPtr(new ImageBuffer(surface.release())); 1643 1644 buffer->context()->scale(static_cast<float>(scaledSize.width()) / size.width(), 1645 static_cast<float>(scaledSize.height()) / size.height()); 1646 1647 return buffer.release(); 1648} 1649 1650void GraphicsContext::setPathFromConvexPoints(SkPath* path, size_t numPoints, const FloatPoint* points) 1651{ 1652 path->incReserve(numPoints); 1653 path->moveTo(WebCoreFloatToSkScalar(points[0].x()), 1654 WebCoreFloatToSkScalar(points[0].y())); 1655 for (size_t i = 1; i < numPoints; ++i) { 1656 path->lineTo(WebCoreFloatToSkScalar(points[i].x()), 1657 WebCoreFloatToSkScalar(points[i].y())); 1658 } 1659 1660 /* The code used to just blindly call this 1661 path->setIsConvex(true); 1662 But webkit can sometimes send us non-convex 4-point values, so we mark the path's 1663 convexity as unknown, so it will get computed by skia at draw time. 1664 See crbug.com 108605 1665 */ 1666 SkPath::Convexity convexity = SkPath::kConvex_Convexity; 1667 if (numPoints == 4) 1668 convexity = SkPath::kUnknown_Convexity; 1669 path->setConvexity(convexity); 1670} 1671 1672void GraphicsContext::drawOuterPath(const SkPath& path, SkPaint& paint, int width) 1673{ 1674#if OS(MACOSX) 1675 paint.setAlpha(64); 1676 paint.setStrokeWidth(width); 1677 paint.setPathEffect(SkCornerPathEffect::Create((width - 1) * 0.5f))->unref(); 1678#else 1679 paint.setStrokeWidth(1); 1680 paint.setPathEffect(SkCornerPathEffect::Create(1))->unref(); 1681#endif 1682 drawPath(path, paint); 1683} 1684 1685void GraphicsContext::drawInnerPath(const SkPath& path, SkPaint& paint, int width) 1686{ 1687#if OS(MACOSX) 1688 paint.setAlpha(128); 1689 paint.setStrokeWidth(width * 0.5f); 1690 drawPath(path, paint); 1691#endif 1692} 1693 1694void GraphicsContext::setRadii(SkVector* radii, IntSize topLeft, IntSize topRight, IntSize bottomRight, IntSize bottomLeft) 1695{ 1696 radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()), 1697 SkIntToScalar(topLeft.height())); 1698 radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()), 1699 SkIntToScalar(topRight.height())); 1700 radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()), 1701 SkIntToScalar(bottomRight.height())); 1702 radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()), 1703 SkIntToScalar(bottomLeft.height())); 1704} 1705 1706PassRefPtr<SkColorFilter> GraphicsContext::WebCoreColorFilterToSkiaColorFilter(ColorFilter colorFilter) 1707{ 1708 switch (colorFilter) { 1709 case ColorFilterLuminanceToAlpha: 1710 return adoptRef(SkLumaColorFilter::Create()); 1711 case ColorFilterLinearRGBToSRGB: 1712 return ImageBuffer::createColorSpaceFilter(ColorSpaceLinearRGB, ColorSpaceDeviceRGB); 1713 case ColorFilterSRGBToLinearRGB: 1714 return ImageBuffer::createColorSpaceFilter(ColorSpaceDeviceRGB, ColorSpaceLinearRGB); 1715 case ColorFilterNone: 1716 break; 1717 default: 1718 ASSERT_NOT_REACHED(); 1719 break; 1720 } 1721 1722 return nullptr; 1723} 1724 1725#if !OS(MACOSX) 1726void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index) 1727{ 1728 const SkPMColor lineColor = lineColors(index); 1729 const SkPMColor antiColor1 = antiColors1(index); 1730 const SkPMColor antiColor2 = antiColors2(index); 1731 1732 uint32_t* row1 = bitmap->getAddr32(0, 0); 1733 uint32_t* row2 = bitmap->getAddr32(0, 1); 1734 uint32_t* row3 = bitmap->getAddr32(0, 2); 1735 uint32_t* row4 = bitmap->getAddr32(0, 3); 1736 1737 // Pattern: X0o o0X0o o0 1738 // XX0o o0XXX0o o0X 1739 // o0XXX0o o0XXX0o 1740 // o0X0o o0X0o 1741 const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0, 0, 0, antiColor2, antiColor1 }; 1742 const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor2, 0, antiColor2, antiColor1, lineColor }; 1743 const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor, lineColor, lineColor, antiColor1, antiColor2 }; 1744 const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 }; 1745 1746 for (int x = 0; x < bitmap->width() + 8; x += 8) { 1747 int count = std::min(bitmap->width() - x, 8); 1748 if (count > 0) { 1749 memcpy(row1 + x, row1Color, count * sizeof(SkPMColor)); 1750 memcpy(row2 + x, row2Color, count * sizeof(SkPMColor)); 1751 memcpy(row3 + x, row3Color, count * sizeof(SkPMColor)); 1752 memcpy(row4 + x, row4Color, count * sizeof(SkPMColor)); 1753 } 1754 } 1755} 1756 1757void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index) 1758{ 1759 const uint32_t lineColor = lineColors(index); 1760 const uint32_t antiColor = antiColors2(index); 1761 1762 // Pattern: X o o X o o X 1763 // o X o o X o 1764 uint32_t* row1 = bitmap->getAddr32(0, 0); 1765 uint32_t* row2 = bitmap->getAddr32(0, 1); 1766 for (int x = 0; x < bitmap->width(); x++) { 1767 switch (x % 4) { 1768 case 0: 1769 row1[x] = lineColor; 1770 break; 1771 case 1: 1772 row1[x] = antiColor; 1773 row2[x] = antiColor; 1774 break; 1775 case 2: 1776 row2[x] = lineColor; 1777 break; 1778 case 3: 1779 row1[x] = antiColor; 1780 row2[x] = antiColor; 1781 break; 1782 } 1783 } 1784} 1785 1786const SkPMColor GraphicsContext::lineColors(int index) 1787{ 1788 static const SkPMColor colors[] = { 1789 SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red. 1790 SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray. 1791 }; 1792 1793 return colors[index]; 1794} 1795 1796const SkPMColor GraphicsContext::antiColors1(int index) 1797{ 1798 static const SkPMColor colors[] = { 1799 SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red. 1800 SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0) // Semitransparent gray. 1801 }; 1802 1803 return colors[index]; 1804} 1805 1806const SkPMColor GraphicsContext::antiColors2(int index) 1807{ 1808 static const SkPMColor colors[] = { 1809 SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red 1810 SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0) // More transparent gray 1811 }; 1812 1813 return colors[index]; 1814} 1815#endif 1816 1817void GraphicsContext::didDrawTextInRect(const SkRect& textRect) 1818{ 1819 if (m_trackTextRegion) { 1820 TRACE_EVENT0("skia", "GraphicsContext::didDrawTextInRect"); 1821 m_textRegion.join(textRect); 1822 } 1823} 1824 1825} 1826