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