2009-01-09 Dimitri Glazkov <dglazkov@chromium.org>
[WebKit-https.git] / WebCore / platform / graphics / skia / PlatformContextSkia.cpp
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 "NativeImageSkia.h"
35 #include "PlatformContextSkia.h"
36 #include "SkiaUtils.h"
37
38 #include "skia/ext/image_operations.h"
39 #include "skia/ext/platform_canvas.h"
40
41 #include "SkBitmap.h"
42 #include "SkColorPriv.h"
43 #include "SkShader.h"
44 #include "SkDashPathEffect.h"
45
46 #include <wtf/MathExtras.h>
47
48 #if defined(__linux__)
49 #include "GdkSkia.h"
50 #endif
51
52 // State -----------------------------------------------------------------------
53
54 // Encapsulates the additional painting state information we store for each
55 // pushed graphics state.
56 struct PlatformContextSkia::State {
57     State();
58     State(const State&);
59     ~State();
60
61     // Common shader state.
62     float m_alpha;
63     SkPorterDuff::Mode m_porterDuffMode;
64     SkShader* m_gradient;
65     SkShader* m_pattern;
66     bool m_useAntialiasing;
67     SkDrawLooper* m_looper;
68
69     // Fill.
70     SkColor m_fillColor;
71
72     // Stroke.
73     WebCore::StrokeStyle m_strokeStyle;
74     SkColor m_strokeColor;
75     float m_strokeThickness;
76     int m_dashRatio;  // Ratio of the length of a dash to its width.
77     float m_miterLimit;
78     SkPaint::Cap m_lineCap;
79     SkPaint::Join m_lineJoin;
80     SkDashPathEffect* m_dash;
81
82     // Text. (See cTextFill & friends in GraphicsContext.h.)
83     int m_textDrawingMode;
84
85     // Helper function for applying the state's alpha value to the given input
86     // color to produce a new output color.
87     SkColor applyAlpha(SkColor) const;
88
89 private:
90     // Not supported.
91     void operator=(const State&);
92 };
93
94 // Note: Keep theses default values in sync with GraphicsContextState.
95 PlatformContextSkia::State::State()
96     : m_alpha(1)
97     , m_porterDuffMode(SkPorterDuff::kSrcOver_Mode)
98     , m_gradient(0)
99     , m_pattern(0)
100     , m_useAntialiasing(true)
101     , m_looper(0)
102     , m_fillColor(0xFF000000)
103     , m_strokeStyle(WebCore::SolidStroke)
104     , m_strokeColor(WebCore::Color::black)
105     , m_strokeThickness(0)
106     , m_dashRatio(3)
107     , m_miterLimit(4)
108     , m_lineCap(SkPaint::kDefault_Cap)
109     , m_lineJoin(SkPaint::kDefault_Join)
110     , m_dash(0)
111     , m_textDrawingMode(WebCore::cTextFill)
112 {
113 }
114
115 PlatformContextSkia::State::State(const State& other)
116 {
117     memcpy(this, &other, sizeof(State));
118
119     m_looper->safeRef();
120     m_dash->safeRef();
121     m_gradient->safeRef();
122     m_pattern->safeRef();
123 }
124
125 PlatformContextSkia::State::~State()
126 {
127     m_looper->safeUnref();
128     m_dash->safeUnref();
129     m_gradient->safeUnref();
130     m_pattern->safeUnref();
131 }
132
133 SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const
134 {
135     int s = roundf(m_alpha * 256);
136     if (s >= 256)
137         return c;
138     if (s < 0)
139         return 0;
140
141     int a = SkAlphaMul(SkColorGetA(c), s);
142     return (c & 0x00FFFFFF) | (a << 24);
143 }
144
145 // PlatformContextSkia ---------------------------------------------------------
146
147 // Danger: canvas can be NULL.
148 PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas)
149     : m_canvas(canvas)
150     , m_stateStack(sizeof(State))
151 {
152     m_stateStack.append(State());
153     m_state = &m_stateStack.last();
154 #if defined(OS_LINUX)
155     m_gdkskia = m_canvas ? gdk_skia_new(m_canvas) : 0;
156 #endif
157 }
158
159 PlatformContextSkia::~PlatformContextSkia()
160 {
161 #if defined(OS_LINUX)
162     if (m_gdkskia) {
163         g_object_unref(m_gdkskia);
164         m_gdkskia = 0;
165     }
166 #endif
167 }
168
169 void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas)
170 {
171     m_canvas = canvas;
172 }
173
174 void PlatformContextSkia::save()
175 {
176     m_stateStack.append(*m_state);
177     m_state = &m_stateStack.last();
178
179     // Save our native canvas.
180     canvas()->save();
181 }
182
183 void PlatformContextSkia::restore()
184 {
185     m_stateStack.removeLast();
186     m_state = &m_stateStack.last();
187
188     // Restore our native canvas.
189     canvas()->restore();
190 }
191
192 void PlatformContextSkia::drawRect(SkRect rect)
193 {
194     SkPaint paint;
195     int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000;
196     if (fillcolorNotTransparent) {
197         setupPaintForFilling(&paint);
198         canvas()->drawRect(rect, paint);
199     }
200
201     if (m_state->m_strokeStyle != WebCore::NoStroke &&
202         (m_state->m_strokeColor & 0xFF000000)) {
203         if (fillcolorNotTransparent) {
204             // This call is expensive so don't call it unnecessarily.
205             paint.reset();
206         }
207         setupPaintForStroking(&paint, &rect, 0);
208         canvas()->drawRect(rect, paint);
209     }
210 }
211
212 void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
213 {
214 #ifdef SK_DEBUGx
215     {
216         SkPaint defaultPaint;
217         SkASSERT(*paint == defaultPaint);
218     }
219 #endif
220
221     paint->setAntiAlias(m_state->m_useAntialiasing);
222     paint->setPorterDuffXfermode(m_state->m_porterDuffMode);
223     paint->setLooper(m_state->m_looper);
224
225     if (m_state->m_gradient)
226         paint->setShader(m_state->m_gradient);
227     else if (m_state->m_pattern)
228         paint->setShader(m_state->m_pattern);
229 }
230
231 void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
232 {
233     setupPaintCommon(paint);
234     paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
235 }
236
237 float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
238 {
239     setupPaintCommon(paint);
240     float width = m_state->m_strokeThickness;
241
242     // This allows dashing and dotting to work properly for hairline strokes.
243     if (width == 0)
244         width = 1;
245
246     paint->setColor(m_state->applyAlpha(m_state->m_strokeColor));
247     paint->setStyle(SkPaint::kStroke_Style);
248     paint->setStrokeWidth(SkFloatToScalar(width));
249     paint->setStrokeCap(m_state->m_lineCap);
250     paint->setStrokeJoin(m_state->m_lineJoin);
251     paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit));
252
253     if (rect != 0 && (static_cast<int>(roundf(width)) & 1))
254         rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
255
256     if (m_state->m_dash)
257         paint->setPathEffect(m_state->m_dash);
258     else {
259         switch (m_state->m_strokeStyle) {
260         case WebCore::NoStroke:
261         case WebCore::SolidStroke:
262             break;
263         case WebCore::DashedStroke:
264             width = m_state->m_dashRatio * width;
265             // Fall through.
266         case WebCore::DottedStroke:
267             SkScalar dashLength;
268             if (length) {
269                 // Determine about how many dashes or dots we should have.
270                 int numDashes = length / roundf(width);
271                 if (!(numDashes & 1))
272                     numDashes++;    // Make it odd so we end on a dash/dot.
273                 // Use the number of dashes to determine the length of a
274                 // dash/dot, which will be approximately width
275                 dashLength = SkScalarDiv(SkIntToScalar(length), SkIntToScalar(numDashes));
276             } else
277                 dashLength = SkFloatToScalar(width);
278             SkScalar intervals[2] = { dashLength, dashLength };
279             paint->setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
280         }
281     }
282
283     return width;
284 }
285
286 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
287 {
288     SkRefCnt_SafeAssign(m_state->m_looper, dl);
289 }
290
291 void PlatformContextSkia::setMiterLimit(float ml)
292 {
293     m_state->m_miterLimit = ml;
294 }
295
296 void PlatformContextSkia::setAlpha(float alpha)
297 {
298     m_state->m_alpha = alpha;
299 }
300
301 void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
302 {
303     m_state->m_lineCap = lc;
304 }
305
306 void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
307 {
308     m_state->m_lineJoin = lj;
309 }
310
311 void PlatformContextSkia::setPorterDuffMode(SkPorterDuff::Mode pdm)
312 {
313     m_state->m_porterDuffMode = pdm;
314 }
315
316 void PlatformContextSkia::setFillColor(SkColor color)
317 {
318     m_state->m_fillColor = color;
319 }
320
321 SkDrawLooper* PlatformContextSkia::getDrawLooper() const
322 {
323     return m_state->m_looper;
324 }
325
326 WebCore::StrokeStyle PlatformContextSkia::getStrokeStyle() const
327 {
328     return m_state->m_strokeStyle;
329 }
330
331 void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle)
332 {
333     m_state->m_strokeStyle = strokeStyle;
334 }
335
336 void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
337 {
338     m_state->m_strokeColor = strokeColor;
339 }
340
341 float PlatformContextSkia::getStrokeThickness() const
342 {
343     return m_state->m_strokeThickness;
344 }
345
346 void PlatformContextSkia::setStrokeThickness(float thickness)
347 {
348     m_state->m_strokeThickness = thickness;
349 }
350
351 int PlatformContextSkia::getTextDrawingMode() const
352 {
353     return m_state->m_textDrawingMode;
354 }
355
356 void PlatformContextSkia::setTextDrawingMode(int mode)
357 {
358   // cTextClip is never used, so we assert that it isn't set:
359   // https://bugs.webkit.org/show_bug.cgi?id=21898
360   ASSERT((mode & WebCore::cTextClip) == 0);
361   m_state->m_textDrawingMode = mode;
362 }
363
364 void PlatformContextSkia::setUseAntialiasing(bool enable)
365 {
366     m_state->m_useAntialiasing = enable;
367 }
368
369 SkColor PlatformContextSkia::fillColor() const
370 {
371     return m_state->m_fillColor;
372 }
373
374 void PlatformContextSkia::beginPath()
375 {
376     m_path.reset();
377 }
378
379 void PlatformContextSkia::addPath(const SkPath& path)
380 {
381     m_path.addPath(path);
382 }
383
384 void PlatformContextSkia::setFillRule(SkPath::FillType fr)
385 {
386     m_path.setFillType(fr);
387 }
388
389 void PlatformContextSkia::setGradient(SkShader* gradient)
390 {
391     if (gradient != m_state->m_gradient) {
392         m_state->m_gradient->safeUnref();
393         m_state->m_gradient = gradient;
394     }
395 }
396
397 void PlatformContextSkia::setPattern(SkShader* pattern)
398 {
399     if (pattern != m_state->m_pattern) {
400         m_state->m_pattern->safeUnref();
401         m_state->m_pattern = pattern;
402     }
403 }
404
405 void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
406 {
407     if (dash != m_state->m_dash) {
408         m_state->m_dash->safeUnref();
409         m_state->m_dash = dash;
410     }
411 }
412
413 void PlatformContextSkia::paintSkPaint(const SkRect& rect,
414                                        const SkPaint& paint)
415 {
416     m_canvas->drawRect(rect, paint);
417 }
418
419 const SkBitmap* PlatformContextSkia::bitmap() const
420 {
421     return &m_canvas->getDevice()->accessBitmap(false);
422 }
423
424 bool PlatformContextSkia::isPrinting()
425 {
426     return m_canvas->getTopPlatformDevice().IsVectorial();
427 }