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)
125 memcpy(this, &other, sizeof(State));
129 m_gradient->safeRef();
130 m_pattern->safeRef();
133 PlatformContextSkia::State::~State()
135 m_looper->safeUnref();
137 m_gradient->safeUnref();
138 m_pattern->safeUnref();
141 SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const
143 int s = roundf(m_alpha * 256);
149 int a = SkAlphaMul(SkColorGetA(c), s);
150 return (c & 0x00FFFFFF) | (a << 24);
153 // PlatformContextSkia ---------------------------------------------------------
155 // Danger: canvas can be NULL.
156 PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas)
158 , m_stateStack(sizeof(State))
160 , m_drawingToImageBuffer(false)
163 m_stateStack.append(State());
164 m_state = &m_stateStack.last();
165 #if defined(OS_LINUX)
166 m_gdkskia = m_canvas ? gdk_skia_new(m_canvas) : 0;
170 PlatformContextSkia::~PlatformContextSkia()
172 #if defined(OS_LINUX)
174 g_object_unref(m_gdkskia);
180 void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas)
186 void PlatformContextSkia::setDrawingToImageBuffer(bool value)
188 m_drawingToImageBuffer = value;
191 bool PlatformContextSkia::isDrawingToImageBuffer() const
193 return m_drawingToImageBuffer;
197 void PlatformContextSkia::save()
199 m_stateStack.append(*m_state);
200 m_state = &m_stateStack.last();
202 // Save our native canvas.
206 #if defined(__linux__) || PLATFORM(WIN_OS)
207 void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect,
208 const WebCore::ImageBuffer* imageBuffer)
210 // Skia doesn't support clipping to an image, so we create a layer. The next
211 // time restore is invoked the layer and |imageBuffer| are combined to
212 // create the resulting image.
213 m_state->m_clip = rect;
214 SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()),
215 SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) };
217 canvas()->saveLayerAlpha(&bounds, 255,
218 static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
219 // Copy off the image as |imageBuffer| may be deleted before restore is invoked.
220 m_state->m_imageBufferClip = *(imageBuffer->context()->platformContext()->bitmap());
223 void PlatformContextSkia::restore()
225 #if defined(__linux__) || PLATFORM(WIN_OS)
226 if (!m_state->m_imageBufferClip.empty()) {
227 applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip);
232 m_stateStack.removeLast();
233 m_state = &m_stateStack.last();
235 // Restore our native canvas.
239 void PlatformContextSkia::drawRect(SkRect rect)
242 int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000;
243 if (fillcolorNotTransparent) {
244 setupPaintForFilling(&paint);
245 canvas()->drawRect(rect, paint);
248 if (m_state->m_strokeStyle != WebCore::NoStroke &&
249 (m_state->m_strokeColor & 0xFF000000)) {
250 if (fillcolorNotTransparent) {
251 // This call is expensive so don't call it unnecessarily.
254 setupPaintForStroking(&paint, &rect, 0);
255 canvas()->drawRect(rect, paint);
259 void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
263 SkPaint defaultPaint;
264 SkASSERT(*paint == defaultPaint);
268 paint->setAntiAlias(m_state->m_useAntialiasing);
269 paint->setPorterDuffXfermode(m_state->m_porterDuffMode);
270 paint->setLooper(m_state->m_looper);
272 if (m_state->m_gradient)
273 paint->setShader(m_state->m_gradient);
274 else if (m_state->m_pattern)
275 paint->setShader(m_state->m_pattern);
278 void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
280 setupPaintCommon(paint);
281 paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
284 float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
286 setupPaintCommon(paint);
287 float width = m_state->m_strokeThickness;
289 // This allows dashing and dotting to work properly for hairline strokes.
293 paint->setColor(m_state->applyAlpha(m_state->m_strokeColor));
294 paint->setStyle(SkPaint::kStroke_Style);
295 paint->setStrokeWidth(SkFloatToScalar(width));
296 paint->setStrokeCap(m_state->m_lineCap);
297 paint->setStrokeJoin(m_state->m_lineJoin);
298 paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit));
300 if (rect != 0 && (static_cast<int>(roundf(width)) & 1))
301 rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
304 paint->setPathEffect(m_state->m_dash);
306 switch (m_state->m_strokeStyle) {
307 case WebCore::NoStroke:
308 case WebCore::SolidStroke:
310 case WebCore::DashedStroke:
311 width = m_state->m_dashRatio * width;
313 case WebCore::DottedStroke:
316 // Determine about how many dashes or dots we should have.
317 int numDashes = length / roundf(width);
318 if (!(numDashes & 1))
319 numDashes++; // Make it odd so we end on a dash/dot.
320 // Use the number of dashes to determine the length of a
321 // dash/dot, which will be approximately width
322 dashLength = SkScalarDiv(SkIntToScalar(length), SkIntToScalar(numDashes));
324 dashLength = SkFloatToScalar(width);
325 SkScalar intervals[2] = { dashLength, dashLength };
326 paint->setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
333 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
335 SkRefCnt_SafeAssign(m_state->m_looper, dl);
338 void PlatformContextSkia::setMiterLimit(float ml)
340 m_state->m_miterLimit = ml;
343 void PlatformContextSkia::setAlpha(float alpha)
345 m_state->m_alpha = alpha;
348 void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
350 m_state->m_lineCap = lc;
353 void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
355 m_state->m_lineJoin = lj;
358 void PlatformContextSkia::setPorterDuffMode(SkPorterDuff::Mode pdm)
360 m_state->m_porterDuffMode = pdm;
363 void PlatformContextSkia::setFillColor(SkColor color)
365 m_state->m_fillColor = color;
368 SkDrawLooper* PlatformContextSkia::getDrawLooper() const
370 return m_state->m_looper;
373 WebCore::StrokeStyle PlatformContextSkia::getStrokeStyle() const
375 return m_state->m_strokeStyle;
378 void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle)
380 m_state->m_strokeStyle = strokeStyle;
383 void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
385 m_state->m_strokeColor = strokeColor;
388 float PlatformContextSkia::getStrokeThickness() const
390 return m_state->m_strokeThickness;
393 void PlatformContextSkia::setStrokeThickness(float thickness)
395 m_state->m_strokeThickness = thickness;
398 int PlatformContextSkia::getTextDrawingMode() const
400 return m_state->m_textDrawingMode;
403 void PlatformContextSkia::setTextDrawingMode(int mode)
405 // cTextClip is never used, so we assert that it isn't set:
406 // https://bugs.webkit.org/show_bug.cgi?id=21898
407 ASSERT((mode & WebCore::cTextClip) == 0);
408 m_state->m_textDrawingMode = mode;
411 void PlatformContextSkia::setUseAntialiasing(bool enable)
413 m_state->m_useAntialiasing = enable;
416 SkColor PlatformContextSkia::fillColor() const
418 return m_state->m_fillColor;
421 void PlatformContextSkia::beginPath()
426 void PlatformContextSkia::addPath(const SkPath& path)
428 m_path.addPath(path);
431 void PlatformContextSkia::setFillRule(SkPath::FillType fr)
433 m_path.setFillType(fr);
436 void PlatformContextSkia::setGradient(SkShader* gradient)
438 if (gradient != m_state->m_gradient) {
439 m_state->m_gradient->safeUnref();
440 m_state->m_gradient = gradient;
444 void PlatformContextSkia::setPattern(SkShader* pattern)
446 if (pattern != m_state->m_pattern) {
447 m_state->m_pattern->safeUnref();
448 m_state->m_pattern = pattern;
452 void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
454 if (dash != m_state->m_dash) {
455 m_state->m_dash->safeUnref();
456 m_state->m_dash = dash;
460 void PlatformContextSkia::paintSkPaint(const SkRect& rect,
461 const SkPaint& paint)
463 m_canvas->drawRect(rect, paint);
466 const SkBitmap* PlatformContextSkia::bitmap() const
468 return &m_canvas->getDevice()->accessBitmap(false);
471 bool PlatformContextSkia::isPrinting()
473 return m_canvas->getTopPlatformDevice().IsVectorial();
476 #if defined(__linux__) || PLATFORM(WIN_OS)
477 void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, const SkBitmap& imageBuffer)
479 // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we
480 // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping.
482 paint.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode);
483 m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint);