• 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 "GraphicsContext.h"
34 #include "ImageBuffer.h"
35 #include "NativeImageSkia.h"
36 #include "PlatformContextSkia.h"
37 #include "SkiaUtils.h"
38 
39 #include "skia/ext/image_operations.h"
40 #include "skia/ext/platform_canvas.h"
41 
42 #include "SkBitmap.h"
43 #include "SkColorPriv.h"
44 #include "SkShader.h"
45 #include "SkDashPathEffect.h"
46 
47 #include <wtf/MathExtras.h>
48 
49 // State -----------------------------------------------------------------------
50 
51 // Encapsulates the additional painting state information we store for each
52 // pushed graphics state.
53 struct PlatformContextSkia::State {
54     State();
55     State(const State&);
56     ~State();
57 
58     // Common shader state.
59     float m_alpha;
60     SkXfermode::Mode m_xferMode;
61     bool m_useAntialiasing;
62     SkDrawLooper* m_looper;
63 
64     // Fill.
65     SkColor m_fillColor;
66     SkShader* m_fillShader;
67 
68     // Stroke.
69     WebCore::StrokeStyle m_strokeStyle;
70     SkColor m_strokeColor;
71     SkShader* m_strokeShader;
72     float m_strokeThickness;
73     int m_dashRatio;  // Ratio of the length of a dash to its width.
74     float m_miterLimit;
75     SkPaint::Cap m_lineCap;
76     SkPaint::Join m_lineJoin;
77     SkDashPathEffect* m_dash;
78 
79     // Text. (See cTextFill & friends in GraphicsContext.h.)
80     int m_textDrawingMode;
81 
82     // Helper function for applying the state's alpha value to the given input
83     // color to produce a new output color.
84     SkColor applyAlpha(SkColor) const;
85 
86 #if defined(__linux__) || PLATFORM(WIN_OS)
87     // If non-empty, the current State is clipped to this image.
88     SkBitmap m_imageBufferClip;
89     // If m_imageBufferClip is non-empty, this is the region the image is clipped to.
90     WebCore::FloatRect m_clip;
91 #endif
92 
93 private:
94     // Not supported.
95     void operator=(const State&);
96 };
97 
98 // Note: Keep theses default values in sync with GraphicsContextState.
State()99 PlatformContextSkia::State::State()
100     : m_alpha(1)
101     , m_xferMode(SkXfermode::kSrcOver_Mode)
102     , m_useAntialiasing(true)
103     , m_looper(0)
104     , m_fillColor(0xFF000000)
105     , m_fillShader(0)
106     , m_strokeStyle(WebCore::SolidStroke)
107     , m_strokeColor(WebCore::Color::black)
108     , m_strokeThickness(0)
109     , m_strokeShader(0)
110     , m_dashRatio(3)
111     , m_miterLimit(4)
112     , m_lineCap(SkPaint::kDefault_Cap)
113     , m_lineJoin(SkPaint::kDefault_Join)
114     , m_dash(0)
115     , m_textDrawingMode(WebCore::cTextFill)
116 {
117 }
118 
State(const State & other)119 PlatformContextSkia::State::State(const State& other)
120     : m_alpha(other.m_alpha)
121     , m_xferMode(other.m_xferMode)
122     , m_useAntialiasing(other.m_useAntialiasing)
123     , m_looper(other.m_looper)
124     , m_fillColor(other.m_fillColor)
125     , m_fillShader(other.m_fillShader)
126     , m_strokeStyle(other.m_strokeStyle)
127     , m_strokeColor(other.m_strokeColor)
128     , m_strokeThickness(other.m_strokeThickness)
129     , m_strokeShader(other.m_strokeShader)
130     , m_dashRatio(other.m_dashRatio)
131     , m_miterLimit(other.m_miterLimit)
132     , m_lineCap(other.m_lineCap)
133     , m_lineJoin(other.m_lineJoin)
134     , m_dash(other.m_dash)
135     , m_textDrawingMode(other.m_textDrawingMode)
136 #if defined(__linux__) || PLATFORM(WIN_OS)
137     , m_imageBufferClip(other.m_imageBufferClip)
138     , m_clip(other.m_clip)
139 #endif
140 {
141     // Up the ref count of these. saveRef does nothing if 'this' is NULL.
142     m_looper->safeRef();
143     m_dash->safeRef();
144     m_fillShader->safeRef();
145     m_strokeShader->safeRef();
146 }
147 
~State()148 PlatformContextSkia::State::~State()
149 {
150     m_looper->safeUnref();
151     m_dash->safeUnref();
152     m_fillShader->safeUnref();
153     m_strokeShader->safeUnref();
154 }
155 
applyAlpha(SkColor c) const156 SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const
157 {
158     int s = roundf(m_alpha * 256);
159     if (s >= 256)
160         return c;
161     if (s < 0)
162         return 0;
163 
164     int a = SkAlphaMul(SkColorGetA(c), s);
165     return (c & 0x00FFFFFF) | (a << 24);
166 }
167 
168 // PlatformContextSkia ---------------------------------------------------------
169 
170 // Danger: canvas can be NULL.
PlatformContextSkia(skia::PlatformCanvas * canvas)171 PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas)
172     : m_canvas(canvas)
173 #if PLATFORM(WIN_OS)
174     , m_drawingToImageBuffer(false)
175 #endif
176 {
177     m_stateStack.append(State());
178     m_state = &m_stateStack.last();
179 }
180 
~PlatformContextSkia()181 PlatformContextSkia::~PlatformContextSkia()
182 {
183 }
184 
setCanvas(skia::PlatformCanvas * canvas)185 void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas)
186 {
187     m_canvas = canvas;
188 }
189 
190 #if PLATFORM(WIN_OS)
setDrawingToImageBuffer(bool value)191 void PlatformContextSkia::setDrawingToImageBuffer(bool value)
192 {
193     m_drawingToImageBuffer = value;
194 }
195 
isDrawingToImageBuffer() const196 bool PlatformContextSkia::isDrawingToImageBuffer() const
197 {
198     return m_drawingToImageBuffer;
199 }
200 #endif
201 
save()202 void PlatformContextSkia::save()
203 {
204     m_stateStack.append(*m_state);
205     m_state = &m_stateStack.last();
206 
207 #if defined(__linux__) || PLATFORM(WIN_OS)
208     // The clip image only needs to be applied once. Reset the image so that we
209     // don't attempt to clip multiple times.
210     m_state->m_imageBufferClip.reset();
211 #endif
212 
213     // Save our native canvas.
214     canvas()->save();
215 }
216 
217 #if defined(__linux__) || PLATFORM(WIN_OS)
beginLayerClippedToImage(const WebCore::FloatRect & rect,const WebCore::ImageBuffer * imageBuffer)218 void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect,
219                                                    const WebCore::ImageBuffer* imageBuffer)
220 {
221     // Skia doesn't support clipping to an image, so we create a layer. The next
222     // time restore is invoked the layer and |imageBuffer| are combined to
223     // create the resulting image.
224     m_state->m_clip = rect;
225     SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()),
226                       SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) };
227 
228     canvas()->saveLayerAlpha(&bounds, 255,
229                              static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
230     // Copy off the image as |imageBuffer| may be deleted before restore is invoked.
231     const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap();
232     if (!bitmap->pixelRef()) {
233         // The bitmap owns it's pixels. This happens when we've allocated the
234         // pixels in some way and assigned them directly to the bitmap (as
235         // happens when we allocate a DIB). In this case the assignment operator
236         // does not copy the pixels, rather the copied bitmap ends up
237         // referencing the same pixels. As the pixels may not live as long as we
238         // need it to, we copy the image.
239         bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config);
240     } else {
241         // If there is a pixel ref, we can safely use the assignment operator.
242         m_state->m_imageBufferClip = *bitmap;
243     }
244 }
245 #endif
246 
restore()247 void PlatformContextSkia::restore()
248 {
249 #if defined(__linux__) || PLATFORM(WIN_OS)
250     if (!m_state->m_imageBufferClip.empty()) {
251         applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip);
252         canvas()->restore();
253     }
254 #endif
255 
256     m_stateStack.removeLast();
257     m_state = &m_stateStack.last();
258 
259     // Restore our native canvas.
260     canvas()->restore();
261 }
262 
drawRect(SkRect rect)263 void PlatformContextSkia::drawRect(SkRect rect)
264 {
265     SkPaint paint;
266     int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000;
267     if (fillcolorNotTransparent) {
268         setupPaintForFilling(&paint);
269         canvas()->drawRect(rect, paint);
270     }
271 
272     if (m_state->m_strokeStyle != WebCore::NoStroke &&
273         (m_state->m_strokeColor & 0xFF000000)) {
274         // We do a fill of four rects to simulate the stroke of a border.
275         SkColor oldFillColor = m_state->m_fillColor;
276 
277         // setFillColor() will set the shader to NULL, so save a ref to it now.
278         SkShader* oldFillShader = m_state->m_fillShader;
279         oldFillShader->safeRef();
280         setFillColor(m_state->m_strokeColor);
281         setupPaintForFilling(&paint);
282         SkRect topBorder = { rect.fLeft, rect.fTop, rect.fRight, rect.fTop + 1 };
283         canvas()->drawRect(topBorder, paint);
284         SkRect bottomBorder = { rect.fLeft, rect.fBottom - 1, rect.fRight, rect.fBottom };
285         canvas()->drawRect(bottomBorder, paint);
286         SkRect leftBorder = { rect.fLeft, rect.fTop + 1, rect.fLeft + 1, rect.fBottom - 1 };
287         canvas()->drawRect(leftBorder, paint);
288         SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, rect.fRight, rect.fBottom - 1 };
289         canvas()->drawRect(rightBorder, paint);
290         setFillColor(oldFillColor);
291         setFillShader(oldFillShader);
292         oldFillShader->safeUnref();
293     }
294 }
295 
setupPaintCommon(SkPaint * paint) const296 void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
297 {
298 #ifdef SK_DEBUGx
299     {
300         SkPaint defaultPaint;
301         SkASSERT(*paint == defaultPaint);
302     }
303 #endif
304 
305     paint->setAntiAlias(m_state->m_useAntialiasing);
306     paint->setXfermodeMode(m_state->m_xferMode);
307     paint->setLooper(m_state->m_looper);
308 }
309 
setupPaintForFilling(SkPaint * paint) const310 void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
311 {
312     setupPaintCommon(paint);
313     paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
314     paint->setShader(m_state->m_fillShader);
315 }
316 
setupPaintForStroking(SkPaint * paint,SkRect * rect,int length) const317 float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
318 {
319     setupPaintCommon(paint);
320     float width = m_state->m_strokeThickness;
321 
322     paint->setColor(m_state->applyAlpha(m_state->m_strokeColor));
323     paint->setShader(m_state->m_strokeShader);
324     paint->setStyle(SkPaint::kStroke_Style);
325     paint->setStrokeWidth(SkFloatToScalar(width));
326     paint->setStrokeCap(m_state->m_lineCap);
327     paint->setStrokeJoin(m_state->m_lineJoin);
328     paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit));
329 
330     if (m_state->m_dash)
331         paint->setPathEffect(m_state->m_dash);
332     else {
333         switch (m_state->m_strokeStyle) {
334         case WebCore::NoStroke:
335         case WebCore::SolidStroke:
336             break;
337         case WebCore::DashedStroke:
338             width = m_state->m_dashRatio * width;
339             // Fall through.
340         case WebCore::DottedStroke:
341             // Truncate the width, since we don't want fuzzy dots or dashes.
342             int dashLength = static_cast<int>(width);
343             // Subtract off the endcaps, since they're rendered separately.
344             int distance = length - 2 * static_cast<int>(m_state->m_strokeThickness);
345             int phase = 1;
346             if (dashLength > 1) {
347                 // Determine how many dashes or dots we should have.
348                 int numDashes = distance / dashLength;
349                 int remainder = distance % dashLength;
350                 // Adjust the phase to center the dashes within the line.
351                 if (numDashes % 2 == 0) {
352                     // Even:  shift right half a dash, minus half the remainder
353                     phase = (dashLength - remainder) / 2;
354                 } else {
355                     // Odd:  shift right a full dash, minus half the remainder
356                     phase = dashLength - remainder / 2;
357                 }
358             }
359             SkScalar dashLengthSk = SkIntToScalar(dashLength);
360             SkScalar intervals[2] = { dashLengthSk, dashLengthSk };
361             paint->setPathEffect(new SkDashPathEffect(intervals, 2, SkIntToScalar(phase)))->unref();
362         }
363     }
364 
365     return width;
366 }
367 
setDrawLooper(SkDrawLooper * dl)368 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
369 {
370     SkRefCnt_SafeAssign(m_state->m_looper, dl);
371 }
372 
setMiterLimit(float ml)373 void PlatformContextSkia::setMiterLimit(float ml)
374 {
375     m_state->m_miterLimit = ml;
376 }
377 
setAlpha(float alpha)378 void PlatformContextSkia::setAlpha(float alpha)
379 {
380     m_state->m_alpha = alpha;
381 }
382 
setLineCap(SkPaint::Cap lc)383 void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
384 {
385     m_state->m_lineCap = lc;
386 }
387 
setLineJoin(SkPaint::Join lj)388 void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
389 {
390     m_state->m_lineJoin = lj;
391 }
392 
setXfermodeMode(SkXfermode::Mode pdm)393 void PlatformContextSkia::setXfermodeMode(SkXfermode::Mode pdm)
394 {
395     m_state->m_xferMode = pdm;
396 }
397 
setFillColor(SkColor color)398 void PlatformContextSkia::setFillColor(SkColor color)
399 {
400     m_state->m_fillColor = color;
401     setFillShader(NULL);
402 }
403 
getDrawLooper() const404 SkDrawLooper* PlatformContextSkia::getDrawLooper() const
405 {
406     return m_state->m_looper;
407 }
408 
getStrokeStyle() const409 WebCore::StrokeStyle PlatformContextSkia::getStrokeStyle() const
410 {
411     return m_state->m_strokeStyle;
412 }
413 
setStrokeStyle(WebCore::StrokeStyle strokeStyle)414 void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle)
415 {
416     m_state->m_strokeStyle = strokeStyle;
417 }
418 
setStrokeColor(SkColor strokeColor)419 void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
420 {
421     m_state->m_strokeColor = strokeColor;
422     setStrokeShader(NULL);
423 }
424 
getStrokeThickness() const425 float PlatformContextSkia::getStrokeThickness() const
426 {
427     return m_state->m_strokeThickness;
428 }
429 
setStrokeThickness(float thickness)430 void PlatformContextSkia::setStrokeThickness(float thickness)
431 {
432     m_state->m_strokeThickness = thickness;
433 }
434 
setStrokeShader(SkShader * strokeShader)435 void PlatformContextSkia::setStrokeShader(SkShader* strokeShader)
436 {
437     if (strokeShader != m_state->m_strokeShader) {
438         m_state->m_strokeShader->safeUnref();
439         m_state->m_strokeShader = strokeShader;
440         m_state->m_strokeShader->safeRef();
441     }
442 }
443 
getTextDrawingMode() const444 int PlatformContextSkia::getTextDrawingMode() const
445 {
446     return m_state->m_textDrawingMode;
447 }
448 
getAlpha() const449 float PlatformContextSkia::getAlpha() const
450 {
451     return m_state->m_alpha;
452 }
453 
setTextDrawingMode(int mode)454 void PlatformContextSkia::setTextDrawingMode(int mode)
455 {
456   // cTextClip is never used, so we assert that it isn't set:
457   // https://bugs.webkit.org/show_bug.cgi?id=21898
458   ASSERT((mode & WebCore::cTextClip) == 0);
459   m_state->m_textDrawingMode = mode;
460 }
461 
setUseAntialiasing(bool enable)462 void PlatformContextSkia::setUseAntialiasing(bool enable)
463 {
464     m_state->m_useAntialiasing = enable;
465 }
466 
effectiveFillColor() const467 SkColor PlatformContextSkia::effectiveFillColor() const
468 {
469     return m_state->applyAlpha(m_state->m_fillColor);
470 }
471 
effectiveStrokeColor() const472 SkColor PlatformContextSkia::effectiveStrokeColor() const
473 {
474     return m_state->applyAlpha(m_state->m_strokeColor);
475 }
476 
beginPath()477 void PlatformContextSkia::beginPath()
478 {
479     m_path.reset();
480 }
481 
addPath(const SkPath & path)482 void PlatformContextSkia::addPath(const SkPath& path)
483 {
484     m_path.addPath(path, m_canvas->getTotalMatrix());
485 }
486 
currentPathInLocalCoordinates() const487 SkPath PlatformContextSkia::currentPathInLocalCoordinates() const
488 {
489     SkPath localPath = m_path;
490     const SkMatrix& matrix = m_canvas->getTotalMatrix();
491     SkMatrix inverseMatrix;
492     if (!matrix.invert(&inverseMatrix))
493         return SkPath();
494     localPath.transform(inverseMatrix);
495     return localPath;
496 }
497 
setFillRule(SkPath::FillType fr)498 void PlatformContextSkia::setFillRule(SkPath::FillType fr)
499 {
500     m_path.setFillType(fr);
501 }
502 
setFillShader(SkShader * fillShader)503 void PlatformContextSkia::setFillShader(SkShader* fillShader)
504 {
505     if (fillShader != m_state->m_fillShader) {
506         m_state->m_fillShader->safeUnref();
507         m_state->m_fillShader = fillShader;
508         m_state->m_fillShader->safeRef();
509     }
510 }
511 
setDashPathEffect(SkDashPathEffect * dash)512 void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
513 {
514     if (dash != m_state->m_dash) {
515         m_state->m_dash->safeUnref();
516         m_state->m_dash = dash;
517     }
518 }
519 
paintSkPaint(const SkRect & rect,const SkPaint & paint)520 void PlatformContextSkia::paintSkPaint(const SkRect& rect,
521                                        const SkPaint& paint)
522 {
523     m_canvas->drawRect(rect, paint);
524 }
525 
bitmap() const526 const SkBitmap* PlatformContextSkia::bitmap() const
527 {
528     return &m_canvas->getDevice()->accessBitmap(false);
529 }
530 
isPrinting()531 bool PlatformContextSkia::isPrinting()
532 {
533     return m_canvas->getTopPlatformDevice().IsVectorial();
534 }
535 
536 #if defined(__linux__) || PLATFORM(WIN_OS)
applyClipFromImage(const WebCore::FloatRect & rect,const SkBitmap & imageBuffer)537 void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, const SkBitmap& imageBuffer)
538 {
539     // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we
540     // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping.
541     SkPaint paint;
542     paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
543     m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint);
544 }
545 #endif
546