2 * Copyright (c) 2008, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "PlatformContextSkia.h"
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"
44 #include "TilingData.h"
46 #include "skia/ext/image_operations.h"
47 #include "skia/ext/platform_canvas.h"
50 #include "SkColorPriv.h"
51 #include "SkDashPathEffect.h"
55 #include "GrContext.h"
56 #include "SkGpuDevice.h"
57 #include "SkGpuDeviceFactory.h"
60 #include <wtf/MathExtras.h>
61 #include <wtf/OwnArrayPtr.h>
62 #include <wtf/Vector.h>
64 #if ENABLE(ACCELERATED_2D_CANVAS)
65 #include "GLES2Canvas.h"
66 #include "SharedGraphicsContext3D.h"
71 extern bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path);
73 // State -----------------------------------------------------------------------
75 // Encapsulates the additional painting state information we store for each
76 // pushed graphics state.
77 struct PlatformContextSkia::State {
82 // Common shader state.
84 SkXfermode::Mode m_xferMode;
85 bool m_useAntialiasing;
86 SkDrawLooper* m_looper;
90 SkShader* m_fillShader;
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.
99 SkPaint::Cap m_lineCap;
100 SkPaint::Join m_lineJoin;
101 SkDashPathEffect* m_dash;
103 // Text. (See TextModeFill & friends in GraphicsContext.h.)
104 TextDrawingModeFlags m_textDrawingMode;
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;
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.
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;
120 // If we currently have a canvas (non-antialiased path) clip applied.
121 bool m_canvasClipApplied;
123 PlatformContextSkia::State cloneInheritedProperties();
126 void operator=(const State&);
129 // Note: Keep theses default values in sync with GraphicsContextState.
130 PlatformContextSkia::State::State()
132 , m_xferMode(SkXfermode::kSrcOver_Mode)
133 , m_useAntialiasing(true)
135 , m_fillColor(0xFF000000)
137 , m_strokeStyle(SolidStroke)
138 , m_strokeColor(Color::black)
140 , m_strokeThickness(0)
143 , m_lineCap(SkPaint::kDefault_Cap)
144 , m_lineJoin(SkPaint::kDefault_Join)
146 , m_textDrawingMode(TextModeFill)
147 , m_interpolationQuality(InterpolationHigh)
148 , m_canvasClipApplied(false)
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)
175 // Up the ref count of these. SkSafeRef does nothing if its argument is 0.
178 SkSafeRef(m_fillShader);
179 SkSafeRef(m_strokeShader);
182 PlatformContextSkia::State::~State()
184 SkSafeUnref(m_looper);
186 SkSafeUnref(m_fillShader);
187 SkSafeUnref(m_strokeShader);
190 // Returns a new State with all of this object's inherited properties copied.
191 PlatformContextSkia::State PlatformContextSkia::State::cloneInheritedProperties()
193 PlatformContextSkia::State state(*this);
195 // Everything is inherited except for the clip paths.
196 state.m_antiAliasClipPaths.clear();
201 SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const
203 int s = roundf(m_alpha * 256);
209 int a = SkAlphaMul(SkColorGetA(c), s);
210 return (c & 0x00FFFFFF) | (a << 24);
213 // PlatformContextSkia ---------------------------------------------------------
215 // Danger: canvas can be NULL.
216 PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas)
218 , m_drawingToImageBuffer(false)
220 #if ENABLE(ACCELERATED_2D_CANVAS)
223 , m_backingStoreState(None)
225 m_stateStack.append(State());
226 m_state = &m_stateStack.last();
229 PlatformContextSkia::~PlatformContextSkia()
231 #if ENABLE(ACCELERATED_2D_CANVAS)
234 // make sure everything related to this platform context has been flushed
236 m_gpuCanvas->context()->grContext()->flush(0);
238 m_gpuCanvas->drawingBuffer()->setWillPublishCallback(0);
243 void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas)
248 void PlatformContextSkia::setDrawingToImageBuffer(bool value)
250 m_drawingToImageBuffer = value;
253 bool PlatformContextSkia::isDrawingToImageBuffer() const
255 return m_drawingToImageBuffer;
258 void PlatformContextSkia::save()
260 ASSERT(!hasImageResamplingHint());
262 m_stateStack.append(m_state->cloneInheritedProperties());
263 m_state = &m_stateStack.last();
265 // The clip image only needs to be applied once. Reset the image so that we
266 // don't attempt to clip multiple times.
267 m_state->m_imageBufferClip.reset();
269 // Save our native canvas.
273 void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect,
274 const ImageBuffer* imageBuffer)
276 // Skia doesn't support clipping to an image, so we create a layer. The next
277 // time restore is invoked the layer and |imageBuffer| are combined to
278 // create the resulting image.
279 m_state->m_clip = rect;
280 SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()),
281 SkFloatToScalar(rect.maxX()), SkFloatToScalar(rect.maxY()) };
283 canvas()->clipRect(bounds);
284 canvas()->saveLayerAlpha(&bounds, 255,
285 static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
286 // Copy off the image as |imageBuffer| may be deleted before restore is invoked.
287 const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap();
288 if (!bitmap->pixelRef()) {
289 // The bitmap owns it's pixels. This happens when we've allocated the
290 // pixels in some way and assigned them directly to the bitmap (as
291 // happens when we allocate a DIB). In this case the assignment operator
292 // does not copy the pixels, rather the copied bitmap ends up
293 // referencing the same pixels. As the pixels may not live as long as we
294 // need it to, we copy the image.
295 bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config);
297 // If there is a pixel ref, we can safely use the assignment operator.
298 m_state->m_imageBufferClip = *bitmap;
302 void PlatformContextSkia::clipPathAntiAliased(const SkPath& clipPath)
304 // If we are currently tracking any anti-alias clip paths, then we already
305 // have a layer in place and don't need to add another.
306 bool haveLayerOutstanding = m_state->m_antiAliasClipPaths.size();
308 // See comments in applyAntiAliasedClipPaths about how this works.
309 m_state->m_antiAliasClipPaths.append(clipPath);
311 if (!haveLayerOutstanding) {
312 SkRect bounds = clipPath.getBounds();
313 canvas()->saveLayerAlpha(&bounds, 255, static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag));
314 // Guards state modification during clipped operations.
315 // The state is popped in applyAntiAliasedClipPaths().
320 void PlatformContextSkia::restore()
322 if (!m_state->m_imageBufferClip.empty()) {
323 applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip);
327 if (!m_state->m_antiAliasClipPaths.isEmpty())
328 applyAntiAliasedClipPaths(m_state->m_antiAliasClipPaths);
330 m_stateStack.removeLast();
331 m_state = &m_stateStack.last();
333 // Restore our native canvas.
337 void PlatformContextSkia::drawRect(SkRect rect)
340 int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000;
341 if (fillcolorNotTransparent) {
342 setupPaintForFilling(&paint);
343 canvas()->drawRect(rect, paint);
346 if (m_state->m_strokeStyle != NoStroke
347 && (m_state->m_strokeColor & 0xFF000000)) {
348 // We do a fill of four rects to simulate the stroke of a border.
349 SkColor oldFillColor = m_state->m_fillColor;
351 // setFillColor() will set the shader to NULL, so save a ref to it now.
352 SkShader* oldFillShader = m_state->m_fillShader;
353 SkSafeRef(oldFillShader);
354 setFillColor(m_state->m_strokeColor);
356 setupPaintForFilling(&paint);
357 SkRect topBorder = { rect.fLeft, rect.fTop, rect.fRight, rect.fTop + 1 };
358 canvas()->drawRect(topBorder, paint);
359 SkRect bottomBorder = { rect.fLeft, rect.fBottom - 1, rect.fRight, rect.fBottom };
360 canvas()->drawRect(bottomBorder, paint);
361 SkRect leftBorder = { rect.fLeft, rect.fTop + 1, rect.fLeft + 1, rect.fBottom - 1 };
362 canvas()->drawRect(leftBorder, paint);
363 SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, rect.fRight, rect.fBottom - 1 };
364 canvas()->drawRect(rightBorder, paint);
365 setFillColor(oldFillColor);
366 setFillShader(oldFillShader);
367 SkSafeUnref(oldFillShader);
371 void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
373 #if defined(SK_DEBUG)
375 SkPaint defaultPaint;
376 SkASSERT(*paint == defaultPaint);
380 paint->setAntiAlias(m_state->m_useAntialiasing);
381 paint->setXfermodeMode(m_state->m_xferMode);
382 paint->setLooper(m_state->m_looper);
385 void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
387 setupPaintCommon(paint);
388 paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
389 paint->setShader(m_state->m_fillShader);
392 float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
394 setupPaintCommon(paint);
395 float width = m_state->m_strokeThickness;
397 paint->setColor(m_state->applyAlpha(m_state->m_strokeColor));
398 paint->setShader(m_state->m_strokeShader);
399 paint->setStyle(SkPaint::kStroke_Style);
400 paint->setStrokeWidth(SkFloatToScalar(width));
401 paint->setStrokeCap(m_state->m_lineCap);
402 paint->setStrokeJoin(m_state->m_lineJoin);
403 paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit));
406 paint->setPathEffect(m_state->m_dash);
408 switch (m_state->m_strokeStyle) {
413 width = m_state->m_dashRatio * width;
416 // Truncate the width, since we don't want fuzzy dots or dashes.
417 int dashLength = static_cast<int>(width);
418 // Subtract off the endcaps, since they're rendered separately.
419 int distance = length - 2 * static_cast<int>(m_state->m_strokeThickness);
421 if (dashLength > 1) {
422 // Determine how many dashes or dots we should have.
423 int numDashes = distance / dashLength;
424 int remainder = distance % dashLength;
425 // Adjust the phase to center the dashes within the line.
426 if (numDashes % 2 == 0) {
427 // Even: shift right half a dash, minus half the remainder
428 phase = (dashLength - remainder) / 2;
430 // Odd: shift right a full dash, minus half the remainder
431 phase = dashLength - remainder / 2;
434 SkScalar dashLengthSk = SkIntToScalar(dashLength);
435 SkScalar intervals[2] = { dashLengthSk, dashLengthSk };
436 paint->setPathEffect(new SkDashPathEffect(intervals, 2, SkIntToScalar(phase)))->unref();
443 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
445 SkRefCnt_SafeAssign(m_state->m_looper, dl);
448 void PlatformContextSkia::setMiterLimit(float ml)
450 m_state->m_miterLimit = ml;
453 void PlatformContextSkia::setAlpha(float alpha)
455 m_state->m_alpha = alpha;
458 void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
460 m_state->m_lineCap = lc;
463 void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
465 m_state->m_lineJoin = lj;
468 void PlatformContextSkia::setXfermodeMode(SkXfermode::Mode pdm)
470 m_state->m_xferMode = pdm;
473 void PlatformContextSkia::setFillColor(SkColor color)
475 m_state->m_fillColor = color;
479 SkDrawLooper* PlatformContextSkia::getDrawLooper() const
481 return m_state->m_looper;
484 StrokeStyle PlatformContextSkia::getStrokeStyle() const
486 return m_state->m_strokeStyle;
489 void PlatformContextSkia::setStrokeStyle(StrokeStyle strokeStyle)
491 m_state->m_strokeStyle = strokeStyle;
494 void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
496 m_state->m_strokeColor = strokeColor;
500 float PlatformContextSkia::getStrokeThickness() const
502 return m_state->m_strokeThickness;
505 void PlatformContextSkia::setStrokeThickness(float thickness)
507 m_state->m_strokeThickness = thickness;
510 void PlatformContextSkia::setStrokeShader(SkShader* strokeShader)
513 m_state->m_strokeColor = Color::black;
515 if (strokeShader != m_state->m_strokeShader) {
516 SkSafeUnref(m_state->m_strokeShader);
517 m_state->m_strokeShader = strokeShader;
518 SkSafeRef(m_state->m_strokeShader);
522 TextDrawingModeFlags PlatformContextSkia::getTextDrawingMode() const
524 return m_state->m_textDrawingMode;
527 float PlatformContextSkia::getAlpha() const
529 return m_state->m_alpha;
532 int PlatformContextSkia::getNormalizedAlpha() const
534 int alpha = roundf(m_state->m_alpha * 256);
542 void PlatformContextSkia::setTextDrawingMode(TextDrawingModeFlags mode)
544 // TextModeClip is never used, so we assert that it isn't set:
545 // https://bugs.webkit.org/show_bug.cgi?id=21898
546 ASSERT(!(mode & TextModeClip));
547 m_state->m_textDrawingMode = mode;
550 void PlatformContextSkia::setUseAntialiasing(bool enable)
552 m_state->m_useAntialiasing = enable;
555 SkColor PlatformContextSkia::effectiveFillColor() const
557 return m_state->applyAlpha(m_state->m_fillColor);
560 SkColor PlatformContextSkia::effectiveStrokeColor() const
562 return m_state->applyAlpha(m_state->m_strokeColor);
565 void PlatformContextSkia::canvasClipPath(const SkPath& path)
567 m_state->m_canvasClipApplied = true;
568 m_canvas->clipPath(path);
571 void PlatformContextSkia::setFillShader(SkShader* fillShader)
574 m_state->m_fillColor = Color::black;
576 if (fillShader != m_state->m_fillShader) {
577 SkSafeUnref(m_state->m_fillShader);
578 m_state->m_fillShader = fillShader;
579 SkSafeRef(m_state->m_fillShader);
583 InterpolationQuality PlatformContextSkia::interpolationQuality() const
585 return m_state->m_interpolationQuality;
588 void PlatformContextSkia::setInterpolationQuality(InterpolationQuality interpolationQuality)
590 m_state->m_interpolationQuality = interpolationQuality;
593 void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
595 if (dash != m_state->m_dash) {
596 SkSafeUnref(m_state->m_dash);
597 m_state->m_dash = dash;
601 void PlatformContextSkia::paintSkPaint(const SkRect& rect,
602 const SkPaint& paint)
604 m_canvas->drawRect(rect, paint);
607 const SkBitmap* PlatformContextSkia::bitmap() const
609 return &m_canvas->getDevice()->accessBitmap(false);
612 bool PlatformContextSkia::isPrinting()
617 return m_canvas->getTopPlatformDevice().IsVectorial();
621 bool PlatformContextSkia::isNativeFontRenderingAllowed()
626 return m_canvas->getTopPlatformDevice().IsNativeFontRenderingAllowed();
630 void PlatformContextSkia::getImageResamplingHint(IntSize* srcSize, FloatSize* dstSize) const
632 *srcSize = m_imageResamplingHintSrcSize;
633 *dstSize = m_imageResamplingHintDstSize;
636 void PlatformContextSkia::setImageResamplingHint(const IntSize& srcSize, const FloatSize& dstSize)
638 m_imageResamplingHintSrcSize = srcSize;
639 m_imageResamplingHintDstSize = dstSize;
642 void PlatformContextSkia::clearImageResamplingHint()
644 m_imageResamplingHintSrcSize = IntSize();
645 m_imageResamplingHintDstSize = FloatSize();
648 bool PlatformContextSkia::hasImageResamplingHint() const
650 return !m_imageResamplingHintSrcSize.isEmpty() && !m_imageResamplingHintDstSize.isEmpty();
653 void PlatformContextSkia::applyClipFromImage(const FloatRect& rect, const SkBitmap& imageBuffer)
655 // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we
656 // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping.
658 paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
659 m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint);
662 void PlatformContextSkia::applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths)
664 // Anti-aliased clipping:
666 // Skia's clipping is 1-bit only. Consider what would happen if it were 8-bit:
667 // We have a square canvas, filled with white and we declare a circular
668 // clipping path. Then we fill twice with a black rectangle. The fractional
669 // pixels would first get the correct color (white * alpha + black * (1 -
670 // alpha)), but the second fill would apply the alpha to the already
671 // modified color and the result would be too dark.
673 // This, anti-aliased clipping needs to be performed after the drawing has
674 // been done. In order to do this, we create a new layer of the canvas in
675 // clipPathAntiAliased and store the clipping path. All drawing is done to
676 // the layer's bitmap while it's in effect. When WebKit calls restore() to
677 // undo the clipping, this function is called.
679 // Here, we walk the list of clipping paths backwards and, for each, we
680 // clear outside of the clipping path. We only need a single extra layer
681 // for any number of clipping paths.
683 // When we call restore on the SkCanvas, the layer's bitmap is composed
684 // into the layer below and we end up with correct, anti-aliased clipping.
689 paint.setXfermodeMode(SkXfermode::kClear_Mode);
690 paint.setAntiAlias(true);
691 paint.setStyle(SkPaint::kFill_Style);
693 for (size_t i = paths.size() - 1; i < paths.size(); --i) {
694 paths[i].toggleInverseFillType();
695 m_canvas->drawPath(paths[i], paint);
701 bool PlatformContextSkia::canAccelerate() const
703 return !m_state->m_fillShader; // Can't accelerate with a fill gradient or pattern.
706 bool PlatformContextSkia::canvasClipApplied() const
708 return m_state->m_canvasClipApplied;
711 class WillPublishCallbackImpl : public DrawingBuffer::WillPublishCallback {
713 static PassOwnPtr<WillPublishCallback> create(PlatformContextSkia* pcs)
715 return adoptPtr(new WillPublishCallbackImpl(pcs));
718 virtual void willPublish()
720 m_pcs->prepareForHardwareDraw();
724 explicit WillPublishCallbackImpl(PlatformContextSkia* pcs)
729 PlatformContextSkia* m_pcs;
732 void PlatformContextSkia::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* drawingBuffer, const WebCore::IntSize& size)
734 #if ENABLE(ACCELERATED_2D_CANVAS)
735 if (context && drawingBuffer) {
737 m_gpuCanvas = new GLES2Canvas(context, drawingBuffer, size);
738 m_uploadTexture.clear();
739 drawingBuffer->setWillPublishCallback(WillPublishCallbackImpl::create(this));
743 context->makeContextCurrent();
744 m_gpuCanvas->bindFramebuffer();
746 GrContext* gr = context->grContext();
748 drawingBuffer->setGrContext(gr);
750 SkDeviceFactory* factory = new SkGpuDeviceFactory(gr, SkGpuDevice::Current3DApiRenderTarget());
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);
756 syncSoftwareCanvas();
757 m_uploadTexture.clear();
764 void PlatformContextSkia::prepareForSoftwareDraw() const
769 m_gpuCanvas->context()->makeContextCurrent();
774 if (m_backingStoreState == Hardware) {
775 // Depending on the blend mode we need to do one of a few things:
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().
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).
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;
790 readbackHardwareToSoftware();
791 // When we switch back to hardware copy the results, don't composite.
792 m_backingStoreState = Software;
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;
802 } else if (m_backingStoreState == None) {
803 m_backingStoreState = Software;
807 void PlatformContextSkia::prepareForHardwareDraw() const
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);
820 m_backingStoreState = Hardware;
823 void PlatformContextSkia::syncSoftwareCanvas() const
828 m_gpuCanvas->context()->makeContextCurrent();
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;
842 m_backingStoreState = Software;
845 void PlatformContextSkia::markDirtyRect(const IntRect& rect)
850 switch (m_backingStoreState) {
853 m_softwareDirtyRect.unite(rect);
858 ASSERT_NOT_REACHED();
862 void PlatformContextSkia::uploadSoftwareToHardware(CompositeOperator op) const
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());
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.
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);
881 m_softwareDirtyRect.setWidth(0); // Clear dirty rect.
885 void PlatformContextSkia::readbackHardwareToSoftware() const
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);
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);
908 m_softwareDirtyRect.unite(IntRect(0, 0, width, height)); // Mark everything as dirty.
912 } // namespace WebCore