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