2009-03-02 Adam Langley <agl@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 // State -----------------------------------------------------------------------
50
51 // Encapsulates the additional painting state information we store for each
52 // pushed graphics state.
53 struct PlatformContextSkia::State {
54     State();
55     State(const State&);
56     ~State();
57
58     // Common shader state.
59     float m_alpha;
60     SkPorterDuff::Mode m_porterDuffMode;
61     SkShader* m_gradient;
62     SkShader* m_pattern;
63     bool m_useAntialiasing;
64     SkDrawLooper* m_looper;
65
66     // Fill.
67     SkColor m_fillColor;
68
69     // Stroke.
70     WebCore::StrokeStyle m_strokeStyle;
71     SkColor m_strokeColor;
72     float m_strokeThickness;
73     int m_dashRatio;  // Ratio of the length of a dash to its width.
74     float m_miterLimit;
75     SkPaint::Cap m_lineCap;
76     SkPaint::Join m_lineJoin;
77     SkDashPathEffect* m_dash;
78
79     // Text. (See cTextFill & friends in GraphicsContext.h.)
80     int m_textDrawingMode;
81
82     // Helper function for applying the state's alpha value to the given input
83     // color to produce a new output color.
84     SkColor applyAlpha(SkColor) const;
85
86 #if defined(__linux__) || PLATFORM(WIN_OS)
87     // If non-empty, the current State is clipped to this image.
88     SkBitmap m_imageBufferClip;
89     // If m_imageBufferClip is non-empty, this is the region the image is clipped to.
90     WebCore::FloatRect m_clip;
91 #endif
92
93 private:
94     // Not supported.
95     void operator=(const State&);
96 };
97
98 // Note: Keep theses default values in sync with GraphicsContextState.
99 PlatformContextSkia::State::State()
100     : m_alpha(1)
101     , m_porterDuffMode(SkPorterDuff::kSrcOver_Mode)
102     , m_gradient(0)
103     , m_pattern(0)
104     , m_useAntialiasing(true)
105     , m_looper(0)
106     , m_fillColor(0xFF000000)
107     , m_strokeStyle(WebCore::SolidStroke)
108     , m_strokeColor(WebCore::Color::black)
109     , m_strokeThickness(0)
110     , m_dashRatio(3)
111     , m_miterLimit(4)
112     , m_lineCap(SkPaint::kDefault_Cap)
113     , m_lineJoin(SkPaint::kDefault_Join)
114     , m_dash(0)
115     , m_textDrawingMode(WebCore::cTextFill)
116 {
117 }
118
119 PlatformContextSkia::State::State(const State& other)
120     : m_alpha(other.m_alpha)
121     , m_porterDuffMode(other.m_porterDuffMode)
122     , m_gradient(other.m_gradient)
123     , m_pattern(other.m_pattern)
124     , m_useAntialiasing(other.m_useAntialiasing)
125     , m_looper(other.m_looper)
126     , m_fillColor(other.m_fillColor)
127     , m_strokeStyle(other.m_strokeStyle)
128     , m_strokeColor(other.m_strokeColor)
129     , m_strokeThickness(other.m_strokeThickness)
130     , m_dashRatio(other.m_dashRatio)
131     , m_miterLimit(other.m_miterLimit)
132     , m_lineCap(other.m_lineCap)
133     , m_lineJoin(other.m_lineJoin)
134     , m_dash(other.m_dash)
135     , m_textDrawingMode(other.m_textDrawingMode)
136 #if defined(__linux__) || PLATFORM(WIN_OS)
137     , m_imageBufferClip(other.m_imageBufferClip)
138     , m_clip(other.m_clip)
139 #endif
140 {
141     // Up the ref count of these. saveRef does nothing if 'this' is NULL.
142     m_looper->safeRef();
143     m_dash->safeRef();
144     m_gradient->safeRef();
145     m_pattern->safeRef();
146 }
147
148 PlatformContextSkia::State::~State()
149 {
150     m_looper->safeUnref();
151     m_dash->safeUnref();
152     m_gradient->safeUnref();
153     m_pattern->safeUnref();
154 }
155
156 SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const
157 {
158     int s = roundf(m_alpha * 256);
159     if (s >= 256)
160         return c;
161     if (s < 0)
162         return 0;
163
164     int a = SkAlphaMul(SkColorGetA(c), s);
165     return (c & 0x00FFFFFF) | (a << 24);
166 }
167
168 // PlatformContextSkia ---------------------------------------------------------
169
170 // Danger: canvas can be NULL.
171 PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas)
172     : m_canvas(canvas)
173     , m_stateStack(sizeof(State))
174 #if PLATFORM(WIN_OS)
175     , m_drawingToImageBuffer(false)
176 #endif
177 {
178     m_stateStack.append(State());
179     m_state = &m_stateStack.last();
180 }
181
182 PlatformContextSkia::~PlatformContextSkia()
183 {
184 }
185
186 void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas)
187 {
188     m_canvas = canvas;
189 }
190
191 #if PLATFORM(WIN_OS)
192 void PlatformContextSkia::setDrawingToImageBuffer(bool value)
193 {
194     m_drawingToImageBuffer = value;
195 }
196
197 bool PlatformContextSkia::isDrawingToImageBuffer() const
198 {
199     return m_drawingToImageBuffer;
200 }
201 #endif
202
203 void PlatformContextSkia::save()
204 {
205     m_stateStack.append(*m_state);
206     m_state = &m_stateStack.last();
207
208 #if defined(__linux__) || PLATFORM(WIN_OS)
209     // The clip image only needs to be applied once. Reset the image so that we
210     // don't attempt to clip multiple times.
211     m_state->m_imageBufferClip.reset();
212 #endif
213
214     // Save our native canvas.
215     canvas()->save();
216 }
217
218 #if defined(__linux__) || PLATFORM(WIN_OS)
219 void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect,
220                                                    const WebCore::ImageBuffer* imageBuffer)
221 {
222     // Skia doesn't support clipping to an image, so we create a layer. The next
223     // time restore is invoked the layer and |imageBuffer| are combined to
224     // create the resulting image.
225     m_state->m_clip = rect;
226     SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()),
227                       SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) };
228                       
229     canvas()->saveLayerAlpha(&bounds, 255,
230                              static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
231     // Copy off the image as |imageBuffer| may be deleted before restore is invoked.
232     const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap();
233     if (!bitmap->pixelRef()) {
234         // The bitmap owns it's pixels. This happens when we've allocated the
235         // pixels in some way and assigned them directly to the bitmap (as
236         // happens when we allocate a DIB). In this case the assignment operator
237         // does not copy the pixels, rather the copied bitmap ends up
238         // referencing the same pixels. As the pixels may not live as long as we
239         // need it to, we copy the image.
240         bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config);
241     } else {
242         // If there is a pixel ref, we can safely use the assignment operator.
243         m_state->m_imageBufferClip = *bitmap;
244     }
245 }
246 #endif
247
248 void PlatformContextSkia::restore()
249 {
250 #if defined(__linux__) || PLATFORM(WIN_OS)
251     if (!m_state->m_imageBufferClip.empty()) {
252         applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip);
253         canvas()->restore();
254     }
255 #endif
256
257     m_stateStack.removeLast();
258     m_state = &m_stateStack.last();
259
260     // Restore our native canvas.
261     canvas()->restore();
262 }
263
264 void PlatformContextSkia::drawRect(SkRect rect)
265 {
266     SkPaint paint;
267     int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000;
268     if (fillcolorNotTransparent) {
269         setupPaintForFilling(&paint);
270         canvas()->drawRect(rect, paint);
271     }
272
273     if (m_state->m_strokeStyle != WebCore::NoStroke &&
274         (m_state->m_strokeColor & 0xFF000000)) {
275         if (fillcolorNotTransparent) {
276             // This call is expensive so don't call it unnecessarily.
277             paint.reset();
278         }
279         setupPaintForStroking(&paint, &rect, 0);
280         canvas()->drawRect(rect, paint);
281     }
282 }
283
284 void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
285 {
286 #ifdef SK_DEBUGx
287     {
288         SkPaint defaultPaint;
289         SkASSERT(*paint == defaultPaint);
290     }
291 #endif
292
293     paint->setAntiAlias(m_state->m_useAntialiasing);
294     paint->setPorterDuffXfermode(m_state->m_porterDuffMode);
295     paint->setLooper(m_state->m_looper);
296
297     if (m_state->m_gradient)
298         paint->setShader(m_state->m_gradient);
299     else if (m_state->m_pattern)
300         paint->setShader(m_state->m_pattern);
301 }
302
303 void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
304 {
305     setupPaintCommon(paint);
306     paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
307 }
308
309 float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
310 {
311     setupPaintCommon(paint);
312     float width = m_state->m_strokeThickness;
313
314     // This allows dashing and dotting to work properly for hairline strokes.
315     if (width == 0)
316         width = 1;
317
318     paint->setColor(m_state->applyAlpha(m_state->m_strokeColor));
319     paint->setStyle(SkPaint::kStroke_Style);
320     paint->setStrokeWidth(SkFloatToScalar(width));
321     paint->setStrokeCap(m_state->m_lineCap);
322     paint->setStrokeJoin(m_state->m_lineJoin);
323     paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit));
324
325     if (rect != 0 && (static_cast<int>(roundf(width)) & 1))
326         rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
327
328     if (m_state->m_dash)
329         paint->setPathEffect(m_state->m_dash);
330     else {
331         switch (m_state->m_strokeStyle) {
332         case WebCore::NoStroke:
333         case WebCore::SolidStroke:
334             break;
335         case WebCore::DashedStroke:
336             width = m_state->m_dashRatio * width;
337             // Fall through.
338         case WebCore::DottedStroke:
339             SkScalar dashLength;
340             if (length) {
341                 // Determine about how many dashes or dots we should have.
342                 int numDashes = length / roundf(width);
343                 if (!(numDashes & 1))
344                     numDashes++;    // Make it odd so we end on a dash/dot.
345                 // Use the number of dashes to determine the length of a
346                 // dash/dot, which will be approximately width
347                 dashLength = SkScalarDiv(SkIntToScalar(length), SkIntToScalar(numDashes));
348             } else
349                 dashLength = SkFloatToScalar(width);
350             SkScalar intervals[2] = { dashLength, dashLength };
351             paint->setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
352         }
353     }
354
355     return width;
356 }
357
358 void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
359 {
360     SkRefCnt_SafeAssign(m_state->m_looper, dl);
361 }
362
363 void PlatformContextSkia::setMiterLimit(float ml)
364 {
365     m_state->m_miterLimit = ml;
366 }
367
368 void PlatformContextSkia::setAlpha(float alpha)
369 {
370     m_state->m_alpha = alpha;
371 }
372
373 void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
374 {
375     m_state->m_lineCap = lc;
376 }
377
378 void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
379 {
380     m_state->m_lineJoin = lj;
381 }
382
383 void PlatformContextSkia::setPorterDuffMode(SkPorterDuff::Mode pdm)
384 {
385     m_state->m_porterDuffMode = pdm;
386 }
387
388 void PlatformContextSkia::setFillColor(SkColor color)
389 {
390     m_state->m_fillColor = color;
391 }
392
393 SkDrawLooper* PlatformContextSkia::getDrawLooper() const
394 {
395     return m_state->m_looper;
396 }
397
398 WebCore::StrokeStyle PlatformContextSkia::getStrokeStyle() const
399 {
400     return m_state->m_strokeStyle;
401 }
402
403 void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle)
404 {
405     m_state->m_strokeStyle = strokeStyle;
406 }
407
408 void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
409 {
410     m_state->m_strokeColor = strokeColor;
411 }
412
413 float PlatformContextSkia::getStrokeThickness() const
414 {
415     return m_state->m_strokeThickness;
416 }
417
418 void PlatformContextSkia::setStrokeThickness(float thickness)
419 {
420     m_state->m_strokeThickness = thickness;
421 }
422
423 int PlatformContextSkia::getTextDrawingMode() const
424 {
425     return m_state->m_textDrawingMode;
426 }
427
428 void PlatformContextSkia::setTextDrawingMode(int mode)
429 {
430   // cTextClip is never used, so we assert that it isn't set:
431   // https://bugs.webkit.org/show_bug.cgi?id=21898
432   ASSERT((mode & WebCore::cTextClip) == 0);
433   m_state->m_textDrawingMode = mode;
434 }
435
436 void PlatformContextSkia::setUseAntialiasing(bool enable)
437 {
438     m_state->m_useAntialiasing = enable;
439 }
440
441 SkColor PlatformContextSkia::effectiveFillColor() const
442 {
443     return m_state->applyAlpha(m_state->m_fillColor);
444 }
445
446 SkColor PlatformContextSkia::effectiveStrokeColor() const
447 {
448     return m_state->applyAlpha(m_state->m_strokeColor);
449 }
450
451 void PlatformContextSkia::beginPath()
452 {
453     m_path.reset();
454 }
455
456 void PlatformContextSkia::addPath(const SkPath& path)
457 {
458     m_path.addPath(path, m_canvas->getTotalMatrix());
459 }
460
461 SkPath PlatformContextSkia::currentPathInLocalCoordinates() const
462 {
463     SkPath localPath = m_path;
464     const SkMatrix& matrix = m_canvas->getTotalMatrix();
465     SkMatrix inverseMatrix;
466     matrix.invert(&inverseMatrix);
467     localPath.transform(inverseMatrix);
468     return localPath;
469 }
470
471 void PlatformContextSkia::setFillRule(SkPath::FillType fr)
472 {
473     m_path.setFillType(fr);
474 }
475
476 void PlatformContextSkia::setGradient(SkShader* gradient)
477 {
478     if (gradient != m_state->m_gradient) {
479         m_state->m_gradient->safeUnref();
480         m_state->m_gradient = gradient;
481     }
482 }
483
484 void PlatformContextSkia::setPattern(SkShader* pattern)
485 {
486     if (pattern != m_state->m_pattern) {
487         m_state->m_pattern->safeUnref();
488         m_state->m_pattern = pattern;
489     }
490 }
491
492 void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
493 {
494     if (dash != m_state->m_dash) {
495         m_state->m_dash->safeUnref();
496         m_state->m_dash = dash;
497     }
498 }
499
500 void PlatformContextSkia::paintSkPaint(const SkRect& rect,
501                                        const SkPaint& paint)
502 {
503     m_canvas->drawRect(rect, paint);
504 }
505
506 const SkBitmap* PlatformContextSkia::bitmap() const
507 {
508     return &m_canvas->getDevice()->accessBitmap(false);
509 }
510
511 bool PlatformContextSkia::isPrinting()
512 {
513     return m_canvas->getTopPlatformDevice().IsVectorial();
514 }
515
516 #if defined(__linux__) || PLATFORM(WIN_OS)
517 void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, const SkBitmap& imageBuffer)
518 {
519     // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we
520     // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping.
521     SkPaint paint;
522     paint.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode);
523     m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint);
524 }
525 #endif