c517a910c8239faa1bb13e8dab1adbc07936f19e
[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     : 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)
143 #endif
144 {
145     // Up the ref count of these. saveRef does nothing if 'this' is NULL.
146     m_looper->safeRef();
147     m_dash->safeRef();
148     m_gradient->safeRef();
149     m_pattern->safeRef();
150 }
151
152 PlatformContextSkia::State::~State()
153 {
154     m_looper->safeUnref();
155     m_dash->safeUnref();
156     m_gradient->safeUnref();
157     m_pattern->safeUnref();
158 }
159
160 SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const
161 {
162     int s = roundf(m_alpha * 256);
163     if (s >= 256)
164         return c;
165     if (s < 0)
166         return 0;
167
168     int a = SkAlphaMul(SkColorGetA(c), s);
169     return (c & 0x00FFFFFF) | (a << 24);
170 }
171
172 // PlatformContextSkia ---------------------------------------------------------
173
174 // Danger: canvas can be NULL.
175 PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas)
176     : m_canvas(canvas)
177     , m_stateStack(sizeof(State))
178 #if PLATFORM(WIN_OS)
179     , m_drawingToImageBuffer(false)
180 #endif
181 {
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;
186 #endif
187 }
188
189 PlatformContextSkia::~PlatformContextSkia()
190 {
191 #if defined(OS_LINUX)
192     if (m_gdkskia) {
193         g_object_unref(m_gdkskia);
194         m_gdkskia = 0;
195     }
196 #endif
197 }
198
199 void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas)
200 {
201     m_canvas = canvas;
202 }
203
204 #if PLATFORM(WIN_OS)
205 void PlatformContextSkia::setDrawingToImageBuffer(bool value)
206 {
207     m_drawingToImageBuffer = value;
208 }
209
210 bool PlatformContextSkia::isDrawingToImageBuffer() const
211 {
212     return m_drawingToImageBuffer;
213 }
214 #endif
215
216 void PlatformContextSkia::save()
217 {
218     m_stateStack.append(*m_state);
219     m_state = &m_stateStack.last();
220
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();
225 #endif
226
227     // Save our native canvas.
228     canvas()->save();
229 }
230
231 #if defined(__linux__) || PLATFORM(WIN_OS)
232 void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect,
233                                                    const WebCore::ImageBuffer* imageBuffer)
234 {
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()) };
241                       
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);
254     } else {
255         // If there is a pixel ref, we can safely use the assignment operator.
256         m_state->m_imageBufferClip = *bitmap;
257     }
258 }
259 #endif
260
261 void PlatformContextSkia::restore()
262 {
263 #if defined(__linux__) || PLATFORM(WIN_OS)
264     if (!m_state->m_imageBufferClip.empty()) {
265         applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip);
266         canvas()->restore();
267     }
268 #endif
269
270     m_stateStack.removeLast();
271     m_state = &m_stateStack.last();
272
273     // Restore our native canvas.
274     canvas()->restore();
275 }
276
277 void PlatformContextSkia::drawRect(SkRect rect)
278 {
279     SkPaint paint;
280     int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000;
281     if (fillcolorNotTransparent) {
282         setupPaintForFilling(&paint);
283         canvas()->drawRect(rect, paint);
284     }
285
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.
290             paint.reset();
291         }
292         setupPaintForStroking(&paint, &rect, 0);
293         canvas()->drawRect(rect, paint);
294     }
295 }
296
297 void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
298 {
299 #ifdef SK_DEBUGx
300     {
301         SkPaint defaultPaint;
302         SkASSERT(*paint == defaultPaint);
303     }
304 #endif
305
306     paint->setAntiAlias(m_state->m_useAntialiasing);
307     paint->setPorterDuffXfermode(m_state->m_porterDuffMode);
308     paint->setLooper(m_state->m_looper);
309
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);
314 }
315
316 void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
317 {
318     setupPaintCommon(paint);
319     paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
320 }
321
322 float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
323 {
324     setupPaintCommon(paint);
325     float width = m_state->m_strokeThickness;
326
327     // This allows dashing and dotting to work properly for hairline strokes.
328     if (width == 0)
329         width = 1;
330
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));
337
338     if (rect != 0 && (static_cast<int>(roundf(width)) & 1))
339         rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
340
341     if (m_state->m_dash)
342         paint->setPathEffect(m_state->m_dash);
343     else {
344         switch (m_state->m_strokeStyle) {
345         case WebCore::NoStroke:
346         case WebCore::SolidStroke:
347             break;
348         case WebCore::DashedStroke:
349             width = m_state->m_dashRatio * width;
350             // Fall through.
351         case WebCore::DottedStroke:
352             SkScalar dashLength;
353             if (length) {
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));
361             } else
362                 dashLength = SkFloatToScalar(width);
363             SkScalar intervals[2] = { dashLength, dashLength };
364             paint->setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
365         }
366     }
367
368     return width;
369 }
370
371 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
372 {
373     SkRefCnt_SafeAssign(m_state->m_looper, dl);
374 }
375
376 void PlatformContextSkia::setMiterLimit(float ml)
377 {
378     m_state->m_miterLimit = ml;
379 }
380
381 void PlatformContextSkia::setAlpha(float alpha)
382 {
383     m_state->m_alpha = alpha;
384 }
385
386 void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
387 {
388     m_state->m_lineCap = lc;
389 }
390
391 void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
392 {
393     m_state->m_lineJoin = lj;
394 }
395
396 void PlatformContextSkia::setPorterDuffMode(SkPorterDuff::Mode pdm)
397 {
398     m_state->m_porterDuffMode = pdm;
399 }
400
401 void PlatformContextSkia::setFillColor(SkColor color)
402 {
403     m_state->m_fillColor = color;
404 }
405
406 SkDrawLooper* PlatformContextSkia::getDrawLooper() const
407 {
408     return m_state->m_looper;
409 }
410
411 WebCore::StrokeStyle PlatformContextSkia::getStrokeStyle() const
412 {
413     return m_state->m_strokeStyle;
414 }
415
416 void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle)
417 {
418     m_state->m_strokeStyle = strokeStyle;
419 }
420
421 void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
422 {
423     m_state->m_strokeColor = strokeColor;
424 }
425
426 float PlatformContextSkia::getStrokeThickness() const
427 {
428     return m_state->m_strokeThickness;
429 }
430
431 void PlatformContextSkia::setStrokeThickness(float thickness)
432 {
433     m_state->m_strokeThickness = thickness;
434 }
435
436 int PlatformContextSkia::getTextDrawingMode() const
437 {
438     return m_state->m_textDrawingMode;
439 }
440
441 void PlatformContextSkia::setTextDrawingMode(int mode)
442 {
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;
447 }
448
449 void PlatformContextSkia::setUseAntialiasing(bool enable)
450 {
451     m_state->m_useAntialiasing = enable;
452 }
453
454 SkColor PlatformContextSkia::effectiveFillColor() const
455 {
456     return m_state->applyAlpha(m_state->m_fillColor);
457 }
458
459 SkColor PlatformContextSkia::effectiveStrokeColor() const
460 {
461     return m_state->applyAlpha(m_state->m_strokeColor);
462 }
463
464 void PlatformContextSkia::beginPath()
465 {
466     m_path.reset();
467 }
468
469 void PlatformContextSkia::addPath(const SkPath& path)
470 {
471     m_path.addPath(path, m_canvas->getTotalMatrix());
472 }
473
474 SkPath PlatformContextSkia::currentPathInLocalCoordinates() const
475 {
476     SkPath localPath = m_path;
477     const SkMatrix& matrix = m_canvas->getTotalMatrix();
478     SkMatrix inverseMatrix;
479     matrix.invert(&inverseMatrix);
480     localPath.transform(inverseMatrix);
481     return localPath;
482 }
483
484 void PlatformContextSkia::setFillRule(SkPath::FillType fr)
485 {
486     m_path.setFillType(fr);
487 }
488
489 void PlatformContextSkia::setGradient(SkShader* gradient)
490 {
491     if (gradient != m_state->m_gradient) {
492         m_state->m_gradient->safeUnref();
493         m_state->m_gradient = gradient;
494     }
495 }
496
497 void PlatformContextSkia::setPattern(SkShader* pattern)
498 {
499     if (pattern != m_state->m_pattern) {
500         m_state->m_pattern->safeUnref();
501         m_state->m_pattern = pattern;
502     }
503 }
504
505 void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
506 {
507     if (dash != m_state->m_dash) {
508         m_state->m_dash->safeUnref();
509         m_state->m_dash = dash;
510     }
511 }
512
513 void PlatformContextSkia::paintSkPaint(const SkRect& rect,
514                                        const SkPaint& paint)
515 {
516     m_canvas->drawRect(rect, paint);
517 }
518
519 const SkBitmap* PlatformContextSkia::bitmap() const
520 {
521     return &m_canvas->getDevice()->accessBitmap(false);
522 }
523
524 bool PlatformContextSkia::isPrinting()
525 {
526     return m_canvas->getTopPlatformDevice().IsVectorial();
527 }
528
529 #if defined(__linux__) || PLATFORM(WIN_OS)
530 void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, const SkBitmap& imageBuffer)
531 {
532     // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we
533     // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping.
534     SkPaint paint;
535     paint.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode);
536     m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint);
537 }
538 #endif