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 "GraphicsContext.h"
34 #include "ImageBuffer.h"
35 #include "NativeImageSkia.h"
36 #include "PlatformContextSkia.h"
37 #include "SkiaUtils.h"
39 #include "skia/ext/image_operations.h"
40 #include "skia/ext/platform_canvas.h"
43 #include "SkColorPriv.h"
45 #include "SkDashPathEffect.h"
47 #include <wtf/MathExtras.h>
49 #if defined(__linux__)
53 // State -----------------------------------------------------------------------
55 // Encapsulates the additional painting state information we store for each
56 // pushed graphics state.
57 struct PlatformContextSkia::State {
62 // Common shader state.
64 SkPorterDuff::Mode m_porterDuffMode;
67 bool m_useAntialiasing;
68 SkDrawLooper* m_looper;
74 WebCore::StrokeStyle m_strokeStyle;
75 SkColor m_strokeColor;
76 float m_strokeThickness;
77 int m_dashRatio; // Ratio of the length of a dash to its width.
79 SkPaint::Cap m_lineCap;
80 SkPaint::Join m_lineJoin;
81 SkDashPathEffect* m_dash;
83 // Text. (See cTextFill & friends in GraphicsContext.h.)
84 int m_textDrawingMode;
86 // Helper function for applying the state's alpha value to the given input
87 // color to produce a new output color.
88 SkColor applyAlpha(SkColor) const;
90 #if defined(__linux__) || PLATFORM(WIN_OS)
91 // If non-empty, the current State is clipped to this image.
92 SkBitmap m_imageBufferClip;
93 // If m_imageBufferClip is non-empty, this is the region the image is clipped to.
94 WebCore::FloatRect m_clip;
99 void operator=(const State&);
102 // Note: Keep theses default values in sync with GraphicsContextState.
103 PlatformContextSkia::State::State()
105 , m_porterDuffMode(SkPorterDuff::kSrcOver_Mode)
108 , m_useAntialiasing(true)
110 , m_fillColor(0xFF000000)
111 , m_strokeStyle(WebCore::SolidStroke)
112 , m_strokeColor(WebCore::Color::black)
113 , m_strokeThickness(0)
116 , m_lineCap(SkPaint::kDefault_Cap)
117 , m_lineJoin(SkPaint::kDefault_Join)
119 , m_textDrawingMode(WebCore::cTextFill)
123 PlatformContextSkia::State::State(const State& other)
124 : m_alpha(other.m_alpha)
125 , m_porterDuffMode(other.m_porterDuffMode)
126 , m_gradient(other.m_gradient)
127 , m_pattern(other.m_pattern)
128 , m_useAntialiasing(other.m_useAntialiasing)
129 , m_looper(other.m_looper)
130 , m_fillColor(other.m_fillColor)
131 , m_strokeStyle(other.m_strokeStyle)
132 , m_strokeColor(other.m_strokeColor)
133 , m_strokeThickness(other.m_strokeThickness)
134 , m_dashRatio(other.m_dashRatio)
135 , m_miterLimit(other.m_miterLimit)
136 , m_lineCap(other.m_lineCap)
137 , m_lineJoin(other.m_lineJoin)
138 , m_dash(other.m_dash)
139 , m_textDrawingMode(other.m_textDrawingMode)
140 #if defined(__linux__) || PLATFORM(WIN_OS)
141 , m_imageBufferClip(other.m_imageBufferClip)
142 , m_clip(other.m_clip)
145 // Up the ref count of these. saveRef does nothing if 'this' is NULL.
148 m_gradient->safeRef();
149 m_pattern->safeRef();
152 PlatformContextSkia::State::~State()
154 m_looper->safeUnref();
156 m_gradient->safeUnref();
157 m_pattern->safeUnref();
160 SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const
162 int s = roundf(m_alpha * 256);
168 int a = SkAlphaMul(SkColorGetA(c), s);
169 return (c & 0x00FFFFFF) | (a << 24);
172 // PlatformContextSkia ---------------------------------------------------------
174 // Danger: canvas can be NULL.
175 PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas)
177 , m_stateStack(sizeof(State))
179 , m_drawingToImageBuffer(false)
182 m_stateStack.append(State());
183 m_state = &m_stateStack.last();
184 #if defined(OS_LINUX)
185 m_gdkskia = m_canvas ? gdk_skia_new(m_canvas) : 0;
189 PlatformContextSkia::~PlatformContextSkia()
191 #if defined(OS_LINUX)
193 g_object_unref(m_gdkskia);
199 void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas)
205 void PlatformContextSkia::setDrawingToImageBuffer(bool value)
207 m_drawingToImageBuffer = value;
210 bool PlatformContextSkia::isDrawingToImageBuffer() const
212 return m_drawingToImageBuffer;
216 void PlatformContextSkia::save()
218 m_stateStack.append(*m_state);
219 m_state = &m_stateStack.last();
221 #if defined(__linux__) || PLATFORM(WIN_OS)
222 // The clip image only needs to be applied once. Reset the image so that we
223 // don't attempt to clip multiple times.
224 m_state->m_imageBufferClip.reset();
227 // Save our native canvas.
231 #if defined(__linux__) || PLATFORM(WIN_OS)
232 void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect,
233 const WebCore::ImageBuffer* imageBuffer)
235 // Skia doesn't support clipping to an image, so we create a layer. The next
236 // time restore is invoked the layer and |imageBuffer| are combined to
237 // create the resulting image.
238 m_state->m_clip = rect;
239 SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()),
240 SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) };
242 canvas()->saveLayerAlpha(&bounds, 255,
243 static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
244 // Copy off the image as |imageBuffer| may be deleted before restore is invoked.
245 const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap();
246 if (!bitmap->pixelRef()) {
247 // The bitmap owns it's pixels. This happens when we've allocated the
248 // pixels in some way and assigned them directly to the bitmap (as
249 // happens when we allocate a DIB). In this case the assignment operator
250 // does not copy the pixels, rather the copied bitmap ends up
251 // referencing the same pixels. As the pixels may not live as long as we
252 // need it to, we copy the image.
253 bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config);
255 // If there is a pixel ref, we can safely use the assignment operator.
256 m_state->m_imageBufferClip = *bitmap;
261 void PlatformContextSkia::restore()
263 #if defined(__linux__) || PLATFORM(WIN_OS)
264 if (!m_state->m_imageBufferClip.empty()) {
265 applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip);
270 m_stateStack.removeLast();
271 m_state = &m_stateStack.last();
273 // Restore our native canvas.
277 void PlatformContextSkia::drawRect(SkRect rect)
280 int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000;
281 if (fillcolorNotTransparent) {
282 setupPaintForFilling(&paint);
283 canvas()->drawRect(rect, paint);
286 if (m_state->m_strokeStyle != WebCore::NoStroke &&
287 (m_state->m_strokeColor & 0xFF000000)) {
288 if (fillcolorNotTransparent) {
289 // This call is expensive so don't call it unnecessarily.
292 setupPaintForStroking(&paint, &rect, 0);
293 canvas()->drawRect(rect, paint);
297 void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
301 SkPaint defaultPaint;
302 SkASSERT(*paint == defaultPaint);
306 paint->setAntiAlias(m_state->m_useAntialiasing);
307 paint->setPorterDuffXfermode(m_state->m_porterDuffMode);
308 paint->setLooper(m_state->m_looper);
310 if (m_state->m_gradient)
311 paint->setShader(m_state->m_gradient);
312 else if (m_state->m_pattern)
313 paint->setShader(m_state->m_pattern);
316 void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
318 setupPaintCommon(paint);
319 paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
322 float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
324 setupPaintCommon(paint);
325 float width = m_state->m_strokeThickness;
327 // This allows dashing and dotting to work properly for hairline strokes.
331 paint->setColor(m_state->applyAlpha(m_state->m_strokeColor));
332 paint->setStyle(SkPaint::kStroke_Style);
333 paint->setStrokeWidth(SkFloatToScalar(width));
334 paint->setStrokeCap(m_state->m_lineCap);
335 paint->setStrokeJoin(m_state->m_lineJoin);
336 paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit));
338 if (rect != 0 && (static_cast<int>(roundf(width)) & 1))
339 rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
342 paint->setPathEffect(m_state->m_dash);
344 switch (m_state->m_strokeStyle) {
345 case WebCore::NoStroke:
346 case WebCore::SolidStroke:
348 case WebCore::DashedStroke:
349 width = m_state->m_dashRatio * width;
351 case WebCore::DottedStroke:
354 // Determine about how many dashes or dots we should have.
355 int numDashes = length / roundf(width);
356 if (!(numDashes & 1))
357 numDashes++; // Make it odd so we end on a dash/dot.
358 // Use the number of dashes to determine the length of a
359 // dash/dot, which will be approximately width
360 dashLength = SkScalarDiv(SkIntToScalar(length), SkIntToScalar(numDashes));
362 dashLength = SkFloatToScalar(width);
363 SkScalar intervals[2] = { dashLength, dashLength };
364 paint->setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
371 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
373 SkRefCnt_SafeAssign(m_state->m_looper, dl);
376 void PlatformContextSkia::setMiterLimit(float ml)
378 m_state->m_miterLimit = ml;
381 void PlatformContextSkia::setAlpha(float alpha)
383 m_state->m_alpha = alpha;
386 void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
388 m_state->m_lineCap = lc;
391 void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
393 m_state->m_lineJoin = lj;
396 void PlatformContextSkia::setPorterDuffMode(SkPorterDuff::Mode pdm)
398 m_state->m_porterDuffMode = pdm;
401 void PlatformContextSkia::setFillColor(SkColor color)
403 m_state->m_fillColor = color;
406 SkDrawLooper* PlatformContextSkia::getDrawLooper() const
408 return m_state->m_looper;
411 WebCore::StrokeStyle PlatformContextSkia::getStrokeStyle() const
413 return m_state->m_strokeStyle;
416 void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle)
418 m_state->m_strokeStyle = strokeStyle;
421 void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
423 m_state->m_strokeColor = strokeColor;
426 float PlatformContextSkia::getStrokeThickness() const
428 return m_state->m_strokeThickness;
431 void PlatformContextSkia::setStrokeThickness(float thickness)
433 m_state->m_strokeThickness = thickness;
436 int PlatformContextSkia::getTextDrawingMode() const
438 return m_state->m_textDrawingMode;
441 void PlatformContextSkia::setTextDrawingMode(int mode)
443 // cTextClip is never used, so we assert that it isn't set:
444 // https://bugs.webkit.org/show_bug.cgi?id=21898
445 ASSERT((mode & WebCore::cTextClip) == 0);
446 m_state->m_textDrawingMode = mode;
449 void PlatformContextSkia::setUseAntialiasing(bool enable)
451 m_state->m_useAntialiasing = enable;
454 SkColor PlatformContextSkia::fillColor() const
456 return m_state->m_fillColor;
459 void PlatformContextSkia::beginPath()
464 void PlatformContextSkia::addPath(const SkPath& path)
466 m_path.addPath(path);
469 void PlatformContextSkia::setFillRule(SkPath::FillType fr)
471 m_path.setFillType(fr);
474 void PlatformContextSkia::setGradient(SkShader* gradient)
476 if (gradient != m_state->m_gradient) {
477 m_state->m_gradient->safeUnref();
478 m_state->m_gradient = gradient;
482 void PlatformContextSkia::setPattern(SkShader* pattern)
484 if (pattern != m_state->m_pattern) {
485 m_state->m_pattern->safeUnref();
486 m_state->m_pattern = pattern;
490 void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
492 if (dash != m_state->m_dash) {
493 m_state->m_dash->safeUnref();
494 m_state->m_dash = dash;
498 void PlatformContextSkia::paintSkPaint(const SkRect& rect,
499 const SkPaint& paint)
501 m_canvas->drawRect(rect, paint);
504 const SkBitmap* PlatformContextSkia::bitmap() const
506 return &m_canvas->getDevice()->accessBitmap(false);
509 bool PlatformContextSkia::isPrinting()
511 return m_canvas->getTopPlatformDevice().IsVectorial();
514 #if defined(__linux__) || PLATFORM(WIN_OS)
515 void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, const SkBitmap& imageBuffer)
517 // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we
518 // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping.
520 paint.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode);
521 m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint);