• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
State()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 
State(const State & other)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 
~State()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.
cloneInheritedProperties()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 
applyAlpha(SkColor c) const201 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.
PlatformContextSkia(SkCanvas * canvas)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 
~PlatformContextSkia()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 
setCanvas(SkCanvas * canvas)247 void PlatformContextSkia::setCanvas(SkCanvas* canvas)
248 {
249     m_canvas = canvas;
250 }
251 
setDrawingToImageBuffer(bool value)252 void PlatformContextSkia::setDrawingToImageBuffer(bool value)
253 {
254     m_drawingToImageBuffer = value;
255 }
256 
isDrawingToImageBuffer() const257 bool PlatformContextSkia::isDrawingToImageBuffer() const
258 {
259     return m_drawingToImageBuffer;
260 }
261 
save()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 
beginLayerClippedToImage(const FloatRect & rect,const ImageBuffer * imageBuffer)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 
clipPathAntiAliased(const SkPath & clipPath)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 
restore()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 
drawRect(SkRect rect)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 
setupPaintCommon(SkPaint * paint) const375 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 
setupPaintForFilling(SkPaint * paint) const389 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 
setupPaintForStroking(SkPaint * paint,SkRect * rect,int length) const396 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 
setDrawLooper(SkDrawLooper * dl)447 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
448 {
449     SkRefCnt_SafeAssign(m_state->m_looper, dl);
450 }
451 
setMiterLimit(float ml)452 void PlatformContextSkia::setMiterLimit(float ml)
453 {
454     m_state->m_miterLimit = ml;
455 }
456 
setAlpha(float alpha)457 void PlatformContextSkia::setAlpha(float alpha)
458 {
459     m_state->m_alpha = alpha;
460 }
461 
setLineCap(SkPaint::Cap lc)462 void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
463 {
464     m_state->m_lineCap = lc;
465 }
466 
setLineJoin(SkPaint::Join lj)467 void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
468 {
469     m_state->m_lineJoin = lj;
470 }
471 
setXfermodeMode(SkXfermode::Mode pdm)472 void PlatformContextSkia::setXfermodeMode(SkXfermode::Mode pdm)
473 {
474     m_state->m_xferMode = pdm;
475 }
476 
setFillColor(SkColor color)477 void PlatformContextSkia::setFillColor(SkColor color)
478 {
479     m_state->m_fillColor = color;
480     setFillShader(0);
481 }
482 
getDrawLooper() const483 SkDrawLooper* PlatformContextSkia::getDrawLooper() const
484 {
485     return m_state->m_looper;
486 }
487 
getStrokeStyle() const488 StrokeStyle PlatformContextSkia::getStrokeStyle() const
489 {
490     return m_state->m_strokeStyle;
491 }
492 
setStrokeStyle(StrokeStyle strokeStyle)493 void PlatformContextSkia::setStrokeStyle(StrokeStyle strokeStyle)
494 {
495     m_state->m_strokeStyle = strokeStyle;
496 }
497 
setStrokeColor(SkColor strokeColor)498 void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
499 {
500     m_state->m_strokeColor = strokeColor;
501     setStrokeShader(0);
502 }
503 
getStrokeThickness() const504 float PlatformContextSkia::getStrokeThickness() const
505 {
506     return m_state->m_strokeThickness;
507 }
508 
setStrokeThickness(float thickness)509 void PlatformContextSkia::setStrokeThickness(float thickness)
510 {
511     m_state->m_strokeThickness = thickness;
512 }
513 
setStrokeShader(SkShader * strokeShader)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 
getTextDrawingMode() const526 TextDrawingModeFlags PlatformContextSkia::getTextDrawingMode() const
527 {
528     return m_state->m_textDrawingMode;
529 }
530 
getAlpha() const531 float PlatformContextSkia::getAlpha() const
532 {
533     return m_state->m_alpha;
534 }
535 
getNormalizedAlpha() const536 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 
setTextDrawingMode(TextDrawingModeFlags mode)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 
setUseAntialiasing(bool enable)554 void PlatformContextSkia::setUseAntialiasing(bool enable)
555 {
556     m_state->m_useAntialiasing = enable;
557 }
558 
effectiveFillColor() const559 SkColor PlatformContextSkia::effectiveFillColor() const
560 {
561     return m_state->applyAlpha(m_state->m_fillColor);
562 }
563 
effectiveStrokeColor() const564 SkColor PlatformContextSkia::effectiveStrokeColor() const
565 {
566     return m_state->applyAlpha(m_state->m_strokeColor);
567 }
568 
canvasClipPath(const SkPath & path)569 void PlatformContextSkia::canvasClipPath(const SkPath& path)
570 {
571     m_state->m_canvasClipApplied = true;
572     m_canvas->clipPath(path);
573 }
574 
setFillShader(SkShader * fillShader)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 
interpolationQuality() const587 InterpolationQuality PlatformContextSkia::interpolationQuality() const
588 {
589     return m_state->m_interpolationQuality;
590 }
591 
setInterpolationQuality(InterpolationQuality interpolationQuality)592 void PlatformContextSkia::setInterpolationQuality(InterpolationQuality interpolationQuality)
593 {
594     m_state->m_interpolationQuality = interpolationQuality;
595 }
596 
setDashPathEffect(SkDashPathEffect * dash)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 
paintSkPaint(const SkRect & rect,const SkPaint & paint)605 void PlatformContextSkia::paintSkPaint(const SkRect& rect,
606                                        const SkPaint& paint)
607 {
608     m_canvas->drawRect(rect, paint);
609 }
610 
bitmap() const611 const SkBitmap* PlatformContextSkia::bitmap() const
612 {
613     return &m_canvas->getDevice()->accessBitmap(false);
614 }
615 
isNativeFontRenderingAllowed()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 
getImageResamplingHint(IntSize * srcSize,FloatSize * dstSize) const625 void PlatformContextSkia::getImageResamplingHint(IntSize* srcSize, FloatSize* dstSize) const
626 {
627     *srcSize = m_imageResamplingHintSrcSize;
628     *dstSize = m_imageResamplingHintDstSize;
629 }
630 
setImageResamplingHint(const IntSize & srcSize,const FloatSize & dstSize)631 void PlatformContextSkia::setImageResamplingHint(const IntSize& srcSize, const FloatSize& dstSize)
632 {
633     m_imageResamplingHintSrcSize = srcSize;
634     m_imageResamplingHintDstSize = dstSize;
635 }
636 
clearImageResamplingHint()637 void PlatformContextSkia::clearImageResamplingHint()
638 {
639     m_imageResamplingHintSrcSize = IntSize();
640     m_imageResamplingHintDstSize = FloatSize();
641 }
642 
hasImageResamplingHint() const643 bool PlatformContextSkia::hasImageResamplingHint() const
644 {
645     return !m_imageResamplingHintSrcSize.isEmpty() && !m_imageResamplingHintDstSize.isEmpty();
646 }
647 
applyClipFromImage(const FloatRect & rect,const SkBitmap & imageBuffer)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 
applyAntiAliasedClipPaths(WTF::Vector<SkPath> & paths)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 
canAccelerate() const696 bool PlatformContextSkia::canAccelerate() const
697 {
698     return !m_state->m_fillShader; // Can't accelerate with a fill gradient or pattern.
699 }
700 
canvasClipApplied() const701 bool PlatformContextSkia::canvasClipApplied() const
702 {
703     return m_state->m_canvasClipApplied;
704 }
705 
706 class WillPublishCallbackImpl : public DrawingBuffer::WillPublishCallback {
707 public:
create(PlatformContextSkia * pcs)708     static PassOwnPtr<WillPublishCallback> create(PlatformContextSkia* pcs)
709     {
710         return adoptPtr(new WillPublishCallbackImpl(pcs));
711     }
712 
willPublish()713     virtual void willPublish()
714     {
715         m_pcs->prepareForHardwareDraw();
716     }
717 
718 private:
WillPublishCallbackImpl(PlatformContextSkia * pcs)719     explicit WillPublishCallbackImpl(PlatformContextSkia* pcs)
720         : m_pcs(pcs)
721     {
722     }
723 
724     PlatformContextSkia* m_pcs;
725 };
726 
setSharedGraphicsContext3D(SharedGraphicsContext3D * context,DrawingBuffer * drawingBuffer,const WebCore::IntSize & size)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 
prepareForSoftwareDraw() const764 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 
prepareForHardwareDraw() const807 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 
syncSoftwareCanvas() const823 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 
markDirtyRect(const IntRect & rect)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 
uploadSoftwareToHardware(CompositeOperator op) const862 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 
readbackHardwareToSoftware() const885 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