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