2009-01-29 Scott Violet <sky@google.com>
[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 "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 #if defined(__linux__)
50 #include "GdkSkia.h"
51 #endif
52
53 // State -----------------------------------------------------------------------
54
55 // Encapsulates the additional painting state information we store for each
56 // pushed graphics state.
57 struct PlatformContextSkia::State {
58     State();
59     State(const State&);
60     ~State();
61
62     // Common shader state.
63     float m_alpha;
64     SkPorterDuff::Mode m_porterDuffMode;
65     SkShader* m_gradient;
66     SkShader* m_pattern;
67     bool m_useAntialiasing;
68     SkDrawLooper* m_looper;
69
70     // Fill.
71     SkColor m_fillColor;
72
73     // Stroke.
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.
78     float m_miterLimit;
79     SkPaint::Cap m_lineCap;
80     SkPaint::Join m_lineJoin;
81     SkDashPathEffect* m_dash;
82
83     // Text. (See cTextFill & friends in GraphicsContext.h.)
84     int m_textDrawingMode;
85
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;
89
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;
95 #endif
96
97 private:
98     // Not supported.
99     void operator=(const State&);
100 };
101
102 // Note: Keep theses default values in sync with GraphicsContextState.
103 PlatformContextSkia::State::State()
104     : m_alpha(1)
105     , m_porterDuffMode(SkPorterDuff::kSrcOver_Mode)
106     , m_gradient(0)
107     , m_pattern(0)
108     , m_useAntialiasing(true)
109     , m_looper(0)
110     , m_fillColor(0xFF000000)
111     , m_strokeStyle(WebCore::SolidStroke)
112     , m_strokeColor(WebCore::Color::black)
113     , m_strokeThickness(0)
114     , m_dashRatio(3)
115     , m_miterLimit(4)
116     , m_lineCap(SkPaint::kDefault_Cap)
117     , m_lineJoin(SkPaint::kDefault_Join)
118     , m_dash(0)
119     , m_textDrawingMode(WebCore::cTextFill)
120 {
121 }
122
123 PlatformContextSkia::State::State(const State& other)
124 {
125     memcpy(this, &other, sizeof(State));
126
127     m_looper->safeRef();
128     m_dash->safeRef();
129     m_gradient->safeRef();
130     m_pattern->safeRef();
131 }
132
133 PlatformContextSkia::State::~State()
134 {
135     m_looper->safeUnref();
136     m_dash->safeUnref();
137     m_gradient->safeUnref();
138     m_pattern->safeUnref();
139 }
140
141 SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const
142 {
143     int s = roundf(m_alpha * 256);
144     if (s >= 256)
145         return c;
146     if (s < 0)
147         return 0;
148
149     int a = SkAlphaMul(SkColorGetA(c), s);
150     return (c & 0x00FFFFFF) | (a << 24);
151 }
152
153 // PlatformContextSkia ---------------------------------------------------------
154
155 // Danger: canvas can be NULL.
156 PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas)
157     : m_canvas(canvas)
158     , m_stateStack(sizeof(State))
159 #if PLATFORM(WIN_OS)
160     , m_drawingToImageBuffer(false)
161 #endif
162 {
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;
167 #endif
168 }
169
170 PlatformContextSkia::~PlatformContextSkia()
171 {
172 #if defined(OS_LINUX)
173     if (m_gdkskia) {
174         g_object_unref(m_gdkskia);
175         m_gdkskia = 0;
176     }
177 #endif
178 }
179
180 void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas)
181 {
182     m_canvas = canvas;
183 }
184
185 #if PLATFORM(WIN_OS)
186 void PlatformContextSkia::setDrawingToImageBuffer(bool value)
187 {
188     m_drawingToImageBuffer = value;
189 }
190
191 bool PlatformContextSkia::isDrawingToImageBuffer() const
192 {
193     return m_drawingToImageBuffer;
194 }
195 #endif
196
197 void PlatformContextSkia::save()
198 {
199     m_stateStack.append(*m_state);
200     m_state = &m_stateStack.last();
201
202     // Save our native canvas.
203     canvas()->save();
204 }
205
206 #if defined(__linux__) || PLATFORM(WIN_OS)
207 void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect,
208                                                    const WebCore::ImageBuffer* imageBuffer)
209 {
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()) };
216                       
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());
221 }
222 #endif
223 void PlatformContextSkia::restore()
224 {
225 #if defined(__linux__) || PLATFORM(WIN_OS)
226     if (!m_state->m_imageBufferClip.empty()) {
227         applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip);
228         canvas()->restore();
229     }
230 #endif
231
232     m_stateStack.removeLast();
233     m_state = &m_stateStack.last();
234
235     // Restore our native canvas.
236     canvas()->restore();
237 }
238
239 void PlatformContextSkia::drawRect(SkRect rect)
240 {
241     SkPaint paint;
242     int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000;
243     if (fillcolorNotTransparent) {
244         setupPaintForFilling(&paint);
245         canvas()->drawRect(rect, paint);
246     }
247
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.
252             paint.reset();
253         }
254         setupPaintForStroking(&paint, &rect, 0);
255         canvas()->drawRect(rect, paint);
256     }
257 }
258
259 void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
260 {
261 #ifdef SK_DEBUGx
262     {
263         SkPaint defaultPaint;
264         SkASSERT(*paint == defaultPaint);
265     }
266 #endif
267
268     paint->setAntiAlias(m_state->m_useAntialiasing);
269     paint->setPorterDuffXfermode(m_state->m_porterDuffMode);
270     paint->setLooper(m_state->m_looper);
271
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);
276 }
277
278 void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
279 {
280     setupPaintCommon(paint);
281     paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
282 }
283
284 float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
285 {
286     setupPaintCommon(paint);
287     float width = m_state->m_strokeThickness;
288
289     // This allows dashing and dotting to work properly for hairline strokes.
290     if (width == 0)
291         width = 1;
292
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));
299
300     if (rect != 0 && (static_cast<int>(roundf(width)) & 1))
301         rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
302
303     if (m_state->m_dash)
304         paint->setPathEffect(m_state->m_dash);
305     else {
306         switch (m_state->m_strokeStyle) {
307         case WebCore::NoStroke:
308         case WebCore::SolidStroke:
309             break;
310         case WebCore::DashedStroke:
311             width = m_state->m_dashRatio * width;
312             // Fall through.
313         case WebCore::DottedStroke:
314             SkScalar dashLength;
315             if (length) {
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));
323             } else
324                 dashLength = SkFloatToScalar(width);
325             SkScalar intervals[2] = { dashLength, dashLength };
326             paint->setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
327         }
328     }
329
330     return width;
331 }
332
333 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
334 {
335     SkRefCnt_SafeAssign(m_state->m_looper, dl);
336 }
337
338 void PlatformContextSkia::setMiterLimit(float ml)
339 {
340     m_state->m_miterLimit = ml;
341 }
342
343 void PlatformContextSkia::setAlpha(float alpha)
344 {
345     m_state->m_alpha = alpha;
346 }
347
348 void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
349 {
350     m_state->m_lineCap = lc;
351 }
352
353 void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
354 {
355     m_state->m_lineJoin = lj;
356 }
357
358 void PlatformContextSkia::setPorterDuffMode(SkPorterDuff::Mode pdm)
359 {
360     m_state->m_porterDuffMode = pdm;
361 }
362
363 void PlatformContextSkia::setFillColor(SkColor color)
364 {
365     m_state->m_fillColor = color;
366 }
367
368 SkDrawLooper* PlatformContextSkia::getDrawLooper() const
369 {
370     return m_state->m_looper;
371 }
372
373 WebCore::StrokeStyle PlatformContextSkia::getStrokeStyle() const
374 {
375     return m_state->m_strokeStyle;
376 }
377
378 void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle)
379 {
380     m_state->m_strokeStyle = strokeStyle;
381 }
382
383 void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
384 {
385     m_state->m_strokeColor = strokeColor;
386 }
387
388 float PlatformContextSkia::getStrokeThickness() const
389 {
390     return m_state->m_strokeThickness;
391 }
392
393 void PlatformContextSkia::setStrokeThickness(float thickness)
394 {
395     m_state->m_strokeThickness = thickness;
396 }
397
398 int PlatformContextSkia::getTextDrawingMode() const
399 {
400     return m_state->m_textDrawingMode;
401 }
402
403 void PlatformContextSkia::setTextDrawingMode(int mode)
404 {
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;
409 }
410
411 void PlatformContextSkia::setUseAntialiasing(bool enable)
412 {
413     m_state->m_useAntialiasing = enable;
414 }
415
416 SkColor PlatformContextSkia::fillColor() const
417 {
418     return m_state->m_fillColor;
419 }
420
421 void PlatformContextSkia::beginPath()
422 {
423     m_path.reset();
424 }
425
426 void PlatformContextSkia::addPath(const SkPath& path)
427 {
428     m_path.addPath(path);
429 }
430
431 void PlatformContextSkia::setFillRule(SkPath::FillType fr)
432 {
433     m_path.setFillType(fr);
434 }
435
436 void PlatformContextSkia::setGradient(SkShader* gradient)
437 {
438     if (gradient != m_state->m_gradient) {
439         m_state->m_gradient->safeUnref();
440         m_state->m_gradient = gradient;
441     }
442 }
443
444 void PlatformContextSkia::setPattern(SkShader* pattern)
445 {
446     if (pattern != m_state->m_pattern) {
447         m_state->m_pattern->safeUnref();
448         m_state->m_pattern = pattern;
449     }
450 }
451
452 void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
453 {
454     if (dash != m_state->m_dash) {
455         m_state->m_dash->safeUnref();
456         m_state->m_dash = dash;
457     }
458 }
459
460 void PlatformContextSkia::paintSkPaint(const SkRect& rect,
461                                        const SkPaint& paint)
462 {
463     m_canvas->drawRect(rect, paint);
464 }
465
466 const SkBitmap* PlatformContextSkia::bitmap() const
467 {
468     return &m_canvas->getDevice()->accessBitmap(false);
469 }
470
471 bool PlatformContextSkia::isPrinting()
472 {
473     return m_canvas->getTopPlatformDevice().IsVectorial();
474 }
475
476 #if defined(__linux__) || PLATFORM(WIN_OS)
477 void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, const SkBitmap& imageBuffer)
478 {
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.
481     SkPaint paint;
482     paint.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode);
483     m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint);
484 }
485 #endif