1 /* 2 * Copyright (c) 2008, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 33 #include "PlatformContextSkia.h" 34 35 #include "AffineTransform.h" 36 #include "DrawingBuffer.h" 37 #include "Extensions3D.h" 38 #include "GraphicsContext.h" 39 #include "GraphicsContext3D.h" 40 #include "ImageBuffer.h" 41 #include "NativeImageSkia.h" 42 #include "SkiaUtils.h" 43 #include "Texture.h" 44 #include "TilingData.h" 45 46 #include "skia/ext/image_operations.h" 47 #include "skia/ext/platform_canvas.h" 48 49 #include "SkBitmap.h" 50 #include "SkColorPriv.h" 51 #include "SkDashPathEffect.h" 52 #include "SkShader.h" 53 54 #if ENABLE(SKIA_GPU) 55 #include "GrContext.h" 56 #include "SkGpuDevice.h" 57 #include "SkGpuDeviceFactory.h" 58 #endif 59 60 #include <wtf/MathExtras.h> 61 #include <wtf/OwnArrayPtr.h> 62 #include <wtf/Vector.h> 63 64 #if ENABLE(ACCELERATED_2D_CANVAS) 65 #include "GLES2Canvas.h" 66 #include "SharedGraphicsContext3D.h" 67 #endif 68 69 namespace WebCore { 70 71 extern bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path); 72 73 // State ----------------------------------------------------------------------- 74 75 // Encapsulates the additional painting state information we store for each 76 // pushed graphics state. 77 struct PlatformContextSkia::State { 78 State(); 79 State(const State&); 80 ~State(); 81 82 // Common shader state. 83 float m_alpha; 84 SkXfermode::Mode m_xferMode; 85 bool m_useAntialiasing; 86 SkDrawLooper* m_looper; 87 88 // Fill. 89 SkColor m_fillColor; 90 SkShader* m_fillShader; 91 92 // Stroke. 93 StrokeStyle m_strokeStyle; 94 SkColor m_strokeColor; 95 SkShader* m_strokeShader; 96 float m_strokeThickness; 97 int m_dashRatio; // Ratio of the length of a dash to its width. 98 float m_miterLimit; 99 SkPaint::Cap m_lineCap; 100 SkPaint::Join m_lineJoin; 101 SkDashPathEffect* m_dash; 102 103 // Text. (See TextModeFill & friends in GraphicsContext.h.) 104 TextDrawingModeFlags m_textDrawingMode; 105 106 // Helper function for applying the state's alpha value to the given input 107 // color to produce a new output color. 108 SkColor applyAlpha(SkColor) const; 109 110 // If non-empty, the current State is clipped to this image. 111 SkBitmap m_imageBufferClip; 112 // If m_imageBufferClip is non-empty, this is the region the image is clipped to. 113 FloatRect m_clip; 114 115 // This is a list of clipping paths which are currently active, in the 116 // order in which they were pushed. 117 WTF::Vector<SkPath> m_antiAliasClipPaths; 118 InterpolationQuality m_interpolationQuality; 119 120 // If we currently have a canvas (non-antialiased path) clip applied. 121 bool m_canvasClipApplied; 122 123 PlatformContextSkia::State cloneInheritedProperties(); 124 private: 125 // Not supported. 126 void operator=(const State&); 127 }; 128 129 // Note: Keep theses default values in sync with GraphicsContextState. 130 PlatformContextSkia::State::State() 131 : m_alpha(1) 132 , m_xferMode(SkXfermode::kSrcOver_Mode) 133 , m_useAntialiasing(true) 134 , m_looper(0) 135 , m_fillColor(0xFF000000) 136 , m_fillShader(0) 137 , m_strokeStyle(SolidStroke) 138 , m_strokeColor(Color::black) 139 , m_strokeShader(0) 140 , m_strokeThickness(0) 141 , m_dashRatio(3) 142 , m_miterLimit(4) 143 , m_lineCap(SkPaint::kDefault_Cap) 144 , m_lineJoin(SkPaint::kDefault_Join) 145 , m_dash(0) 146 , m_textDrawingMode(TextModeFill) 147 , m_interpolationQuality(InterpolationHigh) 148 , m_canvasClipApplied(false) 149 { 150 } 151 152 PlatformContextSkia::State::State(const State& other) 153 : m_alpha(other.m_alpha) 154 , m_xferMode(other.m_xferMode) 155 , m_useAntialiasing(other.m_useAntialiasing) 156 , m_looper(other.m_looper) 157 , m_fillColor(other.m_fillColor) 158 , m_fillShader(other.m_fillShader) 159 , m_strokeStyle(other.m_strokeStyle) 160 , m_strokeColor(other.m_strokeColor) 161 , m_strokeShader(other.m_strokeShader) 162 , m_strokeThickness(other.m_strokeThickness) 163 , m_dashRatio(other.m_dashRatio) 164 , m_miterLimit(other.m_miterLimit) 165 , m_lineCap(other.m_lineCap) 166 , m_lineJoin(other.m_lineJoin) 167 , m_dash(other.m_dash) 168 , m_textDrawingMode(other.m_textDrawingMode) 169 , m_imageBufferClip(other.m_imageBufferClip) 170 , m_clip(other.m_clip) 171 , m_antiAliasClipPaths(other.m_antiAliasClipPaths) 172 , m_interpolationQuality(other.m_interpolationQuality) 173 , m_canvasClipApplied(other.m_canvasClipApplied) 174 { 175 // Up the ref count of these. SkSafeRef does nothing if its argument is 0. 176 SkSafeRef(m_looper); 177 SkSafeRef(m_dash); 178 SkSafeRef(m_fillShader); 179 SkSafeRef(m_strokeShader); 180 } 181 182 PlatformContextSkia::State::~State() 183 { 184 SkSafeUnref(m_looper); 185 SkSafeUnref(m_dash); 186 SkSafeUnref(m_fillShader); 187 SkSafeUnref(m_strokeShader); 188 } 189 190 // Returns a new State with all of this object's inherited properties copied. 191 PlatformContextSkia::State PlatformContextSkia::State::cloneInheritedProperties() 192 { 193 PlatformContextSkia::State state(*this); 194 195 // Everything is inherited except for the clip paths. 196 state.m_antiAliasClipPaths.clear(); 197 198 return state; 199 } 200 201 SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const 202 { 203 int s = roundf(m_alpha * 256); 204 if (s >= 256) 205 return c; 206 if (s < 0) 207 return 0; 208 209 int a = SkAlphaMul(SkColorGetA(c), s); 210 return (c & 0x00FFFFFF) | (a << 24); 211 } 212 213 // PlatformContextSkia --------------------------------------------------------- 214 215 // Danger: canvas can be NULL. 216 PlatformContextSkia::PlatformContextSkia(SkCanvas* canvas) 217 : m_canvas(canvas) 218 , m_printing(false) 219 , m_drawingToImageBuffer(false) 220 , m_useGPU(false) 221 #if ENABLE(ACCELERATED_2D_CANVAS) 222 , m_gpuCanvas(0) 223 #endif 224 , m_backingStoreState(None) 225 { 226 m_stateStack.append(State()); 227 m_state = &m_stateStack.last(); 228 } 229 230 PlatformContextSkia::~PlatformContextSkia() 231 { 232 #if ENABLE(ACCELERATED_2D_CANVAS) 233 if (m_gpuCanvas) { 234 #if ENABLE(SKIA_GPU) 235 // make sure everything related to this platform context has been flushed 236 if (!m_useGPU) { 237 SharedGraphicsContext3D* context = m_gpuCanvas->context(); 238 context->makeContextCurrent(); 239 context->grContext()->flush(0); 240 } 241 #endif 242 m_gpuCanvas->drawingBuffer()->setWillPublishCallback(0); 243 } 244 #endif 245 } 246 247 void PlatformContextSkia::setCanvas(SkCanvas* canvas) 248 { 249 m_canvas = canvas; 250 } 251 252 void PlatformContextSkia::setDrawingToImageBuffer(bool value) 253 { 254 m_drawingToImageBuffer = value; 255 } 256 257 bool PlatformContextSkia::isDrawingToImageBuffer() const 258 { 259 return m_drawingToImageBuffer; 260 } 261 262 void PlatformContextSkia::save() 263 { 264 ASSERT(!hasImageResamplingHint()); 265 266 m_stateStack.append(m_state->cloneInheritedProperties()); 267 m_state = &m_stateStack.last(); 268 269 // The clip image only needs to be applied once. Reset the image so that we 270 // don't attempt to clip multiple times. 271 m_state->m_imageBufferClip.reset(); 272 273 // Save our native canvas. 274 canvas()->save(); 275 } 276 277 void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect, 278 const ImageBuffer* imageBuffer) 279 { 280 // Skia doesn't support clipping to an image, so we create a layer. The next 281 // time restore is invoked the layer and |imageBuffer| are combined to 282 // create the resulting image. 283 m_state->m_clip = rect; 284 SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), 285 SkFloatToScalar(rect.maxX()), SkFloatToScalar(rect.maxY()) }; 286 287 canvas()->clipRect(bounds); 288 canvas()->saveLayerAlpha(&bounds, 255, 289 static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag)); 290 // Copy off the image as |imageBuffer| may be deleted before restore is invoked. 291 const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap(); 292 if (!bitmap->pixelRef()) { 293 // The bitmap owns it's pixels. This happens when we've allocated the 294 // pixels in some way and assigned them directly to the bitmap (as 295 // happens when we allocate a DIB). In this case the assignment operator 296 // does not copy the pixels, rather the copied bitmap ends up 297 // referencing the same pixels. As the pixels may not live as long as we 298 // need it to, we copy the image. 299 bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config); 300 } else { 301 // If there is a pixel ref, we can safely use the assignment operator. 302 m_state->m_imageBufferClip = *bitmap; 303 } 304 } 305 306 void PlatformContextSkia::clipPathAntiAliased(const SkPath& clipPath) 307 { 308 // If we are currently tracking any anti-alias clip paths, then we already 309 // have a layer in place and don't need to add another. 310 bool haveLayerOutstanding = m_state->m_antiAliasClipPaths.size(); 311 312 // See comments in applyAntiAliasedClipPaths about how this works. 313 m_state->m_antiAliasClipPaths.append(clipPath); 314 315 if (!haveLayerOutstanding) { 316 SkRect bounds = clipPath.getBounds(); 317 canvas()->saveLayerAlpha(&bounds, 255, static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag)); 318 // Guards state modification during clipped operations. 319 // The state is popped in applyAntiAliasedClipPaths(). 320 canvas()->save(); 321 } 322 } 323 324 void PlatformContextSkia::restore() 325 { 326 if (!m_state->m_imageBufferClip.empty()) { 327 applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip); 328 canvas()->restore(); 329 } 330 331 if (!m_state->m_antiAliasClipPaths.isEmpty()) 332 applyAntiAliasedClipPaths(m_state->m_antiAliasClipPaths); 333 334 m_stateStack.removeLast(); 335 m_state = &m_stateStack.last(); 336 337 // Restore our native canvas. 338 canvas()->restore(); 339 } 340 341 void PlatformContextSkia::drawRect(SkRect rect) 342 { 343 SkPaint paint; 344 int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000; 345 if (fillcolorNotTransparent) { 346 setupPaintForFilling(&paint); 347 canvas()->drawRect(rect, paint); 348 } 349 350 if (m_state->m_strokeStyle != NoStroke 351 && (m_state->m_strokeColor & 0xFF000000)) { 352 // We do a fill of four rects to simulate the stroke of a border. 353 SkColor oldFillColor = m_state->m_fillColor; 354 355 // setFillColor() will set the shader to NULL, so save a ref to it now. 356 SkShader* oldFillShader = m_state->m_fillShader; 357 SkSafeRef(oldFillShader); 358 setFillColor(m_state->m_strokeColor); 359 paint.reset(); 360 setupPaintForFilling(&paint); 361 SkRect topBorder = { rect.fLeft, rect.fTop, rect.fRight, rect.fTop + 1 }; 362 canvas()->drawRect(topBorder, paint); 363 SkRect bottomBorder = { rect.fLeft, rect.fBottom - 1, rect.fRight, rect.fBottom }; 364 canvas()->drawRect(bottomBorder, paint); 365 SkRect leftBorder = { rect.fLeft, rect.fTop + 1, rect.fLeft + 1, rect.fBottom - 1 }; 366 canvas()->drawRect(leftBorder, paint); 367 SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, rect.fRight, rect.fBottom - 1 }; 368 canvas()->drawRect(rightBorder, paint); 369 setFillColor(oldFillColor); 370 setFillShader(oldFillShader); 371 SkSafeUnref(oldFillShader); 372 } 373 } 374 375 void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const 376 { 377 #if defined(SK_DEBUG) 378 { 379 SkPaint defaultPaint; 380 SkASSERT(*paint == defaultPaint); 381 } 382 #endif 383 384 paint->setAntiAlias(m_state->m_useAntialiasing); 385 paint->setXfermodeMode(m_state->m_xferMode); 386 paint->setLooper(m_state->m_looper); 387 } 388 389 void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const 390 { 391 setupPaintCommon(paint); 392 paint->setColor(m_state->applyAlpha(m_state->m_fillColor)); 393 paint->setShader(m_state->m_fillShader); 394 } 395 396 float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const 397 { 398 setupPaintCommon(paint); 399 float width = m_state->m_strokeThickness; 400 401 paint->setColor(m_state->applyAlpha(m_state->m_strokeColor)); 402 paint->setShader(m_state->m_strokeShader); 403 paint->setStyle(SkPaint::kStroke_Style); 404 paint->setStrokeWidth(SkFloatToScalar(width)); 405 paint->setStrokeCap(m_state->m_lineCap); 406 paint->setStrokeJoin(m_state->m_lineJoin); 407 paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit)); 408 409 if (m_state->m_dash) 410 paint->setPathEffect(m_state->m_dash); 411 else { 412 switch (m_state->m_strokeStyle) { 413 case NoStroke: 414 case SolidStroke: 415 break; 416 case DashedStroke: 417 width = m_state->m_dashRatio * width; 418 // Fall through. 419 case DottedStroke: 420 // Truncate the width, since we don't want fuzzy dots or dashes. 421 int dashLength = static_cast<int>(width); 422 // Subtract off the endcaps, since they're rendered separately. 423 int distance = length - 2 * static_cast<int>(m_state->m_strokeThickness); 424 int phase = 1; 425 if (dashLength > 1) { 426 // Determine how many dashes or dots we should have. 427 int numDashes = distance / dashLength; 428 int remainder = distance % dashLength; 429 // Adjust the phase to center the dashes within the line. 430 if (numDashes % 2 == 0) { 431 // Even: shift right half a dash, minus half the remainder 432 phase = (dashLength - remainder) / 2; 433 } else { 434 // Odd: shift right a full dash, minus half the remainder 435 phase = dashLength - remainder / 2; 436 } 437 } 438 SkScalar dashLengthSk = SkIntToScalar(dashLength); 439 SkScalar intervals[2] = { dashLengthSk, dashLengthSk }; 440 paint->setPathEffect(new SkDashPathEffect(intervals, 2, SkIntToScalar(phase)))->unref(); 441 } 442 } 443 444 return width; 445 } 446 447 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl) 448 { 449 SkRefCnt_SafeAssign(m_state->m_looper, dl); 450 } 451 452 void PlatformContextSkia::setMiterLimit(float ml) 453 { 454 m_state->m_miterLimit = ml; 455 } 456 457 void PlatformContextSkia::setAlpha(float alpha) 458 { 459 m_state->m_alpha = alpha; 460 } 461 462 void PlatformContextSkia::setLineCap(SkPaint::Cap lc) 463 { 464 m_state->m_lineCap = lc; 465 } 466 467 void PlatformContextSkia::setLineJoin(SkPaint::Join lj) 468 { 469 m_state->m_lineJoin = lj; 470 } 471 472 void PlatformContextSkia::setXfermodeMode(SkXfermode::Mode pdm) 473 { 474 m_state->m_xferMode = pdm; 475 } 476 477 void PlatformContextSkia::setFillColor(SkColor color) 478 { 479 m_state->m_fillColor = color; 480 setFillShader(0); 481 } 482 483 SkDrawLooper* PlatformContextSkia::getDrawLooper() const 484 { 485 return m_state->m_looper; 486 } 487 488 StrokeStyle PlatformContextSkia::getStrokeStyle() const 489 { 490 return m_state->m_strokeStyle; 491 } 492 493 void PlatformContextSkia::setStrokeStyle(StrokeStyle strokeStyle) 494 { 495 m_state->m_strokeStyle = strokeStyle; 496 } 497 498 void PlatformContextSkia::setStrokeColor(SkColor strokeColor) 499 { 500 m_state->m_strokeColor = strokeColor; 501 setStrokeShader(0); 502 } 503 504 float PlatformContextSkia::getStrokeThickness() const 505 { 506 return m_state->m_strokeThickness; 507 } 508 509 void PlatformContextSkia::setStrokeThickness(float thickness) 510 { 511 m_state->m_strokeThickness = thickness; 512 } 513 514 void PlatformContextSkia::setStrokeShader(SkShader* strokeShader) 515 { 516 if (strokeShader) 517 m_state->m_strokeColor = Color::black; 518 519 if (strokeShader != m_state->m_strokeShader) { 520 SkSafeUnref(m_state->m_strokeShader); 521 m_state->m_strokeShader = strokeShader; 522 SkSafeRef(m_state->m_strokeShader); 523 } 524 } 525 526 TextDrawingModeFlags PlatformContextSkia::getTextDrawingMode() const 527 { 528 return m_state->m_textDrawingMode; 529 } 530 531 float PlatformContextSkia::getAlpha() const 532 { 533 return m_state->m_alpha; 534 } 535 536 int PlatformContextSkia::getNormalizedAlpha() const 537 { 538 int alpha = roundf(m_state->m_alpha * 256); 539 if (alpha > 255) 540 alpha = 255; 541 else if (alpha < 0) 542 alpha = 0; 543 return alpha; 544 } 545 546 void PlatformContextSkia::setTextDrawingMode(TextDrawingModeFlags mode) 547 { 548 // TextModeClip is never used, so we assert that it isn't set: 549 // https://bugs.webkit.org/show_bug.cgi?id=21898 550 ASSERT(!(mode & TextModeClip)); 551 m_state->m_textDrawingMode = mode; 552 } 553 554 void PlatformContextSkia::setUseAntialiasing(bool enable) 555 { 556 m_state->m_useAntialiasing = enable; 557 } 558 559 SkColor PlatformContextSkia::effectiveFillColor() const 560 { 561 return m_state->applyAlpha(m_state->m_fillColor); 562 } 563 564 SkColor PlatformContextSkia::effectiveStrokeColor() const 565 { 566 return m_state->applyAlpha(m_state->m_strokeColor); 567 } 568 569 void PlatformContextSkia::canvasClipPath(const SkPath& path) 570 { 571 m_state->m_canvasClipApplied = true; 572 m_canvas->clipPath(path); 573 } 574 575 void PlatformContextSkia::setFillShader(SkShader* fillShader) 576 { 577 if (fillShader) 578 m_state->m_fillColor = Color::black; 579 580 if (fillShader != m_state->m_fillShader) { 581 SkSafeUnref(m_state->m_fillShader); 582 m_state->m_fillShader = fillShader; 583 SkSafeRef(m_state->m_fillShader); 584 } 585 } 586 587 InterpolationQuality PlatformContextSkia::interpolationQuality() const 588 { 589 return m_state->m_interpolationQuality; 590 } 591 592 void PlatformContextSkia::setInterpolationQuality(InterpolationQuality interpolationQuality) 593 { 594 m_state->m_interpolationQuality = interpolationQuality; 595 } 596 597 void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash) 598 { 599 if (dash != m_state->m_dash) { 600 SkSafeUnref(m_state->m_dash); 601 m_state->m_dash = dash; 602 } 603 } 604 605 void PlatformContextSkia::paintSkPaint(const SkRect& rect, 606 const SkPaint& paint) 607 { 608 m_canvas->drawRect(rect, paint); 609 } 610 611 const SkBitmap* PlatformContextSkia::bitmap() const 612 { 613 return &m_canvas->getDevice()->accessBitmap(false); 614 } 615 616 bool PlatformContextSkia::isNativeFontRenderingAllowed() 617 { 618 #if ENABLE(SKIA_GPU) 619 return false; 620 #else 621 return skia::SupportsPlatformPaint(m_canvas); 622 #endif 623 } 624 625 void PlatformContextSkia::getImageResamplingHint(IntSize* srcSize, FloatSize* dstSize) const 626 { 627 *srcSize = m_imageResamplingHintSrcSize; 628 *dstSize = m_imageResamplingHintDstSize; 629 } 630 631 void PlatformContextSkia::setImageResamplingHint(const IntSize& srcSize, const FloatSize& dstSize) 632 { 633 m_imageResamplingHintSrcSize = srcSize; 634 m_imageResamplingHintDstSize = dstSize; 635 } 636 637 void PlatformContextSkia::clearImageResamplingHint() 638 { 639 m_imageResamplingHintSrcSize = IntSize(); 640 m_imageResamplingHintDstSize = FloatSize(); 641 } 642 643 bool PlatformContextSkia::hasImageResamplingHint() const 644 { 645 return !m_imageResamplingHintSrcSize.isEmpty() && !m_imageResamplingHintDstSize.isEmpty(); 646 } 647 648 void PlatformContextSkia::applyClipFromImage(const FloatRect& rect, const SkBitmap& imageBuffer) 649 { 650 // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we 651 // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping. 652 SkPaint paint; 653 paint.setXfermodeMode(SkXfermode::kDstIn_Mode); 654 m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint); 655 } 656 657 void PlatformContextSkia::applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths) 658 { 659 // Anti-aliased clipping: 660 // 661 // Skia's clipping is 1-bit only. Consider what would happen if it were 8-bit: 662 // We have a square canvas, filled with white and we declare a circular 663 // clipping path. Then we fill twice with a black rectangle. The fractional 664 // pixels would first get the correct color (white * alpha + black * (1 - 665 // alpha)), but the second fill would apply the alpha to the already 666 // modified color and the result would be too dark. 667 // 668 // This, anti-aliased clipping needs to be performed after the drawing has 669 // been done. In order to do this, we create a new layer of the canvas in 670 // clipPathAntiAliased and store the clipping path. All drawing is done to 671 // the layer's bitmap while it's in effect. When WebKit calls restore() to 672 // undo the clipping, this function is called. 673 // 674 // Here, we walk the list of clipping paths backwards and, for each, we 675 // clear outside of the clipping path. We only need a single extra layer 676 // for any number of clipping paths. 677 // 678 // When we call restore on the SkCanvas, the layer's bitmap is composed 679 // into the layer below and we end up with correct, anti-aliased clipping. 680 681 m_canvas->restore(); 682 683 SkPaint paint; 684 paint.setXfermodeMode(SkXfermode::kClear_Mode); 685 paint.setAntiAlias(true); 686 paint.setStyle(SkPaint::kFill_Style); 687 688 for (size_t i = paths.size() - 1; i < paths.size(); --i) { 689 paths[i].toggleInverseFillType(); 690 m_canvas->drawPath(paths[i], paint); 691 } 692 693 m_canvas->restore(); 694 } 695 696 bool PlatformContextSkia::canAccelerate() const 697 { 698 return !m_state->m_fillShader; // Can't accelerate with a fill gradient or pattern. 699 } 700 701 bool PlatformContextSkia::canvasClipApplied() const 702 { 703 return m_state->m_canvasClipApplied; 704 } 705 706 class WillPublishCallbackImpl : public DrawingBuffer::WillPublishCallback { 707 public: 708 static PassOwnPtr<WillPublishCallback> create(PlatformContextSkia* pcs) 709 { 710 return adoptPtr(new WillPublishCallbackImpl(pcs)); 711 } 712 713 virtual void willPublish() 714 { 715 m_pcs->prepareForHardwareDraw(); 716 } 717 718 private: 719 explicit WillPublishCallbackImpl(PlatformContextSkia* pcs) 720 : m_pcs(pcs) 721 { 722 } 723 724 PlatformContextSkia* m_pcs; 725 }; 726 727 void PlatformContextSkia::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* drawingBuffer, const WebCore::IntSize& size) 728 { 729 #if ENABLE(ACCELERATED_2D_CANVAS) 730 if (context && drawingBuffer) { 731 m_useGPU = true; 732 m_gpuCanvas = new GLES2Canvas(context, drawingBuffer, size); 733 m_uploadTexture.clear(); 734 drawingBuffer->setWillPublishCallback(WillPublishCallbackImpl::create(this)); 735 736 #if ENABLE(SKIA_GPU) 737 m_useGPU = false; 738 context->makeContextCurrent(); 739 m_gpuCanvas->bindFramebuffer(); 740 741 GrContext* gr = context->grContext(); 742 gr->resetContext(); 743 drawingBuffer->setGrContext(gr); 744 745 GrPlatformSurfaceDesc drawBufDesc; 746 drawingBuffer->getGrPlatformSurfaceDesc(&drawBufDesc); 747 GrTexture* drawBufTex = static_cast<GrTexture*>(gr->createPlatformSurface(drawBufDesc)); 748 SkDeviceFactory* factory = new SkGpuDeviceFactory(gr, drawBufTex); 749 drawBufTex->unref(); 750 751 SkDevice* device = factory->newDevice(m_canvas, SkBitmap::kARGB_8888_Config, drawingBuffer->size().width(), drawingBuffer->size().height(), false, false); 752 m_canvas->setDevice(device)->unref(); 753 m_canvas->setDeviceFactory(factory); 754 #endif 755 } else { 756 syncSoftwareCanvas(); 757 m_uploadTexture.clear(); 758 m_gpuCanvas.clear(); 759 m_useGPU = false; 760 } 761 #endif 762 } 763 764 void PlatformContextSkia::prepareForSoftwareDraw() const 765 { 766 if (!m_useGPU) { 767 #if ENABLE(SKIA_GPU) 768 if (m_gpuCanvas) 769 m_gpuCanvas->context()->makeContextCurrent(); 770 #endif 771 return; 772 } 773 774 if (m_backingStoreState == Hardware) { 775 // Depending on the blend mode we need to do one of a few things: 776 777 // * For associative blend modes, we can draw into an initially empty 778 // canvas and then composite the results on top of the hardware drawn 779 // results before the next hardware draw or swapBuffers(). 780 781 // * For non-associative blend modes we have to do a readback and then 782 // software draw. When we re-upload in this mode we have to blow 783 // away whatever is in the hardware backing store (do a copy instead 784 // of a compositing operation). 785 786 if (m_state->m_xferMode == SkXfermode::kSrcOver_Mode) { 787 // Note that we have rendering results in both the hardware and software backing stores. 788 m_backingStoreState = Mixed; 789 } else { 790 readbackHardwareToSoftware(); 791 // When we switch back to hardware copy the results, don't composite. 792 m_backingStoreState = Software; 793 } 794 } else if (m_backingStoreState == Mixed) { 795 if (m_state->m_xferMode != SkXfermode::kSrcOver_Mode) { 796 // Have to composite our currently software drawn data... 797 uploadSoftwareToHardware(CompositeSourceOver); 798 // then do a readback so we can hardware draw stuff. 799 readbackHardwareToSoftware(); 800 m_backingStoreState = Software; 801 } 802 } else if (m_backingStoreState == None) { 803 m_backingStoreState = Software; 804 } 805 } 806 807 void PlatformContextSkia::prepareForHardwareDraw() const 808 { 809 if (!m_useGPU) 810 return; 811 812 if (m_backingStoreState == Software) { 813 // Last drawn in software; upload everything we've drawn. 814 uploadSoftwareToHardware(CompositeCopy); 815 } else if (m_backingStoreState == Mixed) { 816 // Stuff in software/hardware, composite the software stuff on top of 817 // the hardware stuff. 818 uploadSoftwareToHardware(CompositeSourceOver); 819 } 820 m_backingStoreState = Hardware; 821 } 822 823 void PlatformContextSkia::syncSoftwareCanvas() const 824 { 825 if (!m_useGPU) { 826 #if ENABLE(SKIA_GPU) 827 if (m_gpuCanvas) 828 m_gpuCanvas->context()->makeContextCurrent(); 829 #endif 830 return; 831 } 832 833 if (m_backingStoreState == Hardware) 834 readbackHardwareToSoftware(); 835 else if (m_backingStoreState == Mixed) { 836 // Have to composite our currently software drawn data.. 837 uploadSoftwareToHardware(CompositeSourceOver); 838 // then do a readback. 839 readbackHardwareToSoftware(); 840 m_backingStoreState = Software; 841 } 842 m_backingStoreState = Software; 843 } 844 845 void PlatformContextSkia::markDirtyRect(const IntRect& rect) 846 { 847 if (!m_useGPU) 848 return; 849 850 switch (m_backingStoreState) { 851 case Software: 852 case Mixed: 853 m_softwareDirtyRect.unite(rect); 854 return; 855 case Hardware: 856 return; 857 default: 858 ASSERT_NOT_REACHED(); 859 } 860 } 861 862 void PlatformContextSkia::uploadSoftwareToHardware(CompositeOperator op) const 863 { 864 #if ENABLE(ACCELERATED_2D_CANVAS) 865 const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(false); 866 SkAutoLockPixels lock(bitmap); 867 SharedGraphicsContext3D* context = m_gpuCanvas->context(); 868 if (!m_uploadTexture || m_uploadTexture->tiles().totalSizeX() < bitmap.width() || m_uploadTexture->tiles().totalSizeY() < bitmap.height()) 869 m_uploadTexture = context->createTexture(Texture::BGRA8, bitmap.width(), bitmap.height()); 870 871 m_uploadTexture->updateSubRect(bitmap.getPixels(), m_softwareDirtyRect); 872 AffineTransform identity; 873 gpuCanvas()->drawTexturedRect(m_uploadTexture.get(), m_softwareDirtyRect, m_softwareDirtyRect, identity, 1.0, ColorSpaceDeviceRGB, op, false); 874 // Clear out the region of the software canvas we just uploaded. 875 m_canvas->save(); 876 m_canvas->resetMatrix(); 877 SkRect bounds = m_softwareDirtyRect; 878 m_canvas->clipRect(bounds, SkRegion::kReplace_Op); 879 m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); 880 m_canvas->restore(); 881 m_softwareDirtyRect.setWidth(0); // Clear dirty rect. 882 #endif 883 } 884 885 void PlatformContextSkia::readbackHardwareToSoftware() const 886 { 887 #if ENABLE(ACCELERATED_2D_CANVAS) 888 const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(true); 889 SkAutoLockPixels lock(bitmap); 890 int width = bitmap.width(), height = bitmap.height(); 891 OwnArrayPtr<uint32_t> buf = adoptArrayPtr(new uint32_t[width]); 892 SharedGraphicsContext3D* context = m_gpuCanvas->context(); 893 m_gpuCanvas->bindFramebuffer(); 894 // Flips the image vertically. 895 for (int y = 0; y < height; ++y) { 896 uint32_t* pixels = bitmap.getAddr32(0, y); 897 if (context->supportsBGRA()) 898 context->readPixels(0, height - 1 - y, width, 1, Extensions3D::BGRA_EXT, GraphicsContext3D::UNSIGNED_BYTE, pixels); 899 else { 900 context->readPixels(0, height - 1 - y, width, 1, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixels); 901 for (int i = 0; i < width; ++i) { 902 uint32_t pixel = pixels[i]; 903 // Swizzles from RGBA -> BGRA. 904 pixels[i] = (pixel & 0xFF00FF00) | ((pixel & 0x00FF0000) >> 16) | ((pixel & 0x000000FF) << 16); 905 } 906 } 907 } 908 m_softwareDirtyRect.unite(IntRect(0, 0, width, height)); // Mark everything as dirty. 909 #endif 910 } 911 912 } // namespace WebCore 913