OSDN Git Service

Force layers above overscroll/fixed elements to composite
[android-x86/external-webkit.git] / Source / WebCore / platform / graphics / android / GraphicsContextAndroid.cpp
1 /*
2  * Copyright 2006, The Android Open Source Project
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #include "config.h"
26 #include "GraphicsContext.h"
27
28 #include "AffineTransform.h"
29 #include "Gradient.h"
30 #include "NotImplemented.h"
31 #include "Path.h"
32 #include "Pattern.h"
33 #include "PlatformGraphicsContext.h"
34 #include "SkBitmapRef.h"
35 #include "SkBlurDrawLooper.h"
36 #include "SkBlurMaskFilter.h"
37 #include "SkCanvas.h"
38 #include "SkColorPriv.h"
39 #include "SkDashPathEffect.h"
40 #include "SkDevice.h"
41 #include "SkGradientShader.h"
42 #include "SkPaint.h"
43 #include "SkString.h"
44 #include "SkiaUtils.h"
45 #include "TransformationMatrix.h"
46 #include "android_graphics.h"
47
48 using namespace std;
49
50 #define GC2CANVAS(ctx)  (ctx)->m_data->getPlatformGfxCtx()->mCanvas
51
52 namespace WebCore {
53
54 static int RoundToInt(float x)
55 {
56     return (int)roundf(x);
57 }
58
59 template <typename T> T* deepCopyPtr(const T* src)
60 {
61     return src ? new T(*src) : 0;
62 }
63
64 // Set a bitmap shader that mimics dashing by width-on, width-off.
65 // Returns false if it could not succeed (e.g. there was an existing shader)
66 static bool setBitmapDash(SkPaint* paint, int width) {
67     if (width <= 0 || paint->getShader())
68         return false;
69
70     SkColor c = paint->getColor();
71
72     SkBitmap bm;
73     bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1);
74     bm.allocPixels();
75     bm.lockPixels();
76
77     // set the ON pixel
78     *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c),
79                                             SkColorGetG(c), SkColorGetB(c));
80     // set the OFF pixel
81     *bm.getAddr32(1, 0) = 0;
82     bm.unlockPixels();
83
84     SkMatrix matrix;
85     matrix.setScale(SkIntToScalar(width), SK_Scalar1);
86
87     SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
88                                                SkShader::kClamp_TileMode);
89     s->setLocalMatrix(matrix);
90
91     paint->setShader(s)->unref();
92     return true;
93 }
94
95 // TODO / questions
96
97 // alpha: how does this interact with the alpha in Color? multiply them together?
98 // mode: do I always respect this? If so, then
99 // the rgb() & 0xFF000000 check will abort drawing too often
100 // Is Color premultiplied or not? If it is, then I can't blindly pass it to paint.setColor()
101
102 struct ShadowRec {
103     SkScalar blur;
104     SkScalar dx;
105     SkScalar dy;
106     SkColor color;  // alpha>0 means valid shadow
107     ShadowRec(SkScalar b = 0,
108               SkScalar x = 0,
109               SkScalar y = 0,
110               SkColor c = 0) // by default, alpha=0, so no shadow
111             : blur(b), dx(x), dy(y), color(c)
112         {};
113 };
114
115 class GraphicsContextPlatformPrivate {
116 public:
117     struct State {
118         SkPathEffect* pathEffect;
119         float miterLimit;
120         float alpha;
121         float strokeThickness;
122         SkPaint::Cap lineCap;
123         SkPaint::Join lineJoin;
124         SkXfermode::Mode mode;
125         int dashRatio; // Ratio of the length of a dash to its width
126         ShadowRec shadow;
127         SkColor fillColor;
128         SkColor strokeColor;
129         bool useAA;
130         // This is a list of clipping paths which are currently active, in the
131         // order in which they were pushed.
132         WTF::Vector<SkPath> antiAliasClipPaths;
133
134         State()
135             : pathEffect(0)
136             , miterLimit(4)
137             , alpha(1)
138             , strokeThickness(0) // Same as default in GraphicsContextPrivate.h
139             , lineCap(SkPaint::kDefault_Cap)
140             , lineJoin(SkPaint::kDefault_Join)
141             , mode(SkXfermode::kSrcOver_Mode)
142             , dashRatio(3)
143             , fillColor(SK_ColorBLACK)
144             , strokeColor(SK_ColorBLACK)
145             , useAA(true)
146         {
147         }
148
149         State(const State& other)
150             : pathEffect(other.pathEffect)
151             , miterLimit(other.miterLimit)
152             , alpha(other.alpha)
153             , strokeThickness(other.strokeThickness)
154             , lineCap(other.lineCap)
155             , lineJoin(other.lineJoin)
156             , mode(other.mode)
157             , dashRatio(other.dashRatio)
158             , shadow(other.shadow)
159             , fillColor(other.fillColor)
160             , strokeColor(other.strokeColor)
161             , useAA(other.useAA)
162         {
163             SkSafeRef(pathEffect);
164         }
165
166         ~State()
167         {
168             SkSafeUnref(pathEffect);
169         }
170
171         void setShadow(int radius, int dx, int dy, SkColor c)
172         {
173             // Cut the radius in half, to visually match the effect seen in
174             // safari browser
175             shadow.blur = SkScalarHalf(SkIntToScalar(radius));
176             shadow.dx = SkIntToScalar(dx);
177             shadow.dy = SkIntToScalar(dy);
178             shadow.color = c;
179         }
180
181         bool setupShadowPaint(GraphicsContext* ctx, SkPaint* paint, SkPoint* offset)
182         {
183             paint->setAntiAlias(true);
184             paint->setDither(true);
185             paint->setXfermodeMode(mode);
186             paint->setColor(shadow.color);
187             offset->set(shadow.dx, shadow.dy);
188
189             // Currently, only GraphicsContexts associated with the
190             // HTMLCanvasElement have shadows ignore transforms set.  This
191             // allows us to distinguish between CSS and Canvas shadows which
192             // have different rendering specifications.
193             uint32_t flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
194             if (ctx->shadowsIgnoreTransforms()) {
195                 offset->fY = -offset->fY;
196                 flags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag;
197             }
198
199             if (shadow.blur > 0) {
200                 paint->setMaskFilter(SkBlurMaskFilter::Create(shadow.blur,
201                                      SkBlurMaskFilter::kNormal_BlurStyle))->unref();
202             }
203             return SkColorGetA(shadow.color) && (shadow.blur || shadow.dx || shadow.dy);
204         }
205
206         SkColor applyAlpha(SkColor c) const
207         {
208             int s = RoundToInt(alpha * 256);
209             if (s >= 256)
210                 return c;
211             if (s < 0)
212                 return 0;
213
214             int a = SkAlphaMul(SkColorGetA(c), s);
215             return (c & 0x00FFFFFF) | (a << 24);
216         }
217     };
218
219     GraphicsContextPlatformPrivate(GraphicsContext* gfxCtx, PlatformGraphicsContext* platformGfxCtx)
220         : m_parentGfxCtx(gfxCtx)
221         , m_platformGfxCtx(platformGfxCtx)
222         , m_stateStack(sizeof(State))
223     {
224         State* state = static_cast<State*>(m_stateStack.push_back());
225         new (state) State();
226         m_state = state;
227     }
228
229     ~GraphicsContextPlatformPrivate()
230     {
231         // We force restores so we don't leak any subobjects owned by our
232         // stack of State records.
233         while (m_stateStack.count() > 0)
234             this->restore();
235
236         if (m_platformGfxCtx && m_platformGfxCtx->deleteUs())
237             delete m_platformGfxCtx;
238     }
239
240     void save()
241     {
242         State* newState = static_cast<State*>(m_stateStack.push_back());
243         new (newState) State(*m_state);
244         m_state = newState;
245     }
246
247     void restore()
248     {
249         if (!m_state->antiAliasClipPaths.isEmpty())
250             applyAntiAliasedClipPaths(m_state->antiAliasClipPaths);
251
252         m_state->~State();
253         m_stateStack.pop_back();
254         m_state = static_cast<State*>(m_stateStack.back());
255     }
256
257     void setFillColor(const Color& c)
258     {
259         m_state->fillColor = c.rgb();
260     }
261
262     void setStrokeColor(const Color& c)
263     {
264         m_state->strokeColor = c.rgb();
265     }
266
267     void setStrokeThickness(float f)
268     {
269         m_state->strokeThickness = f;
270     }
271
272     void setupPaintCommon(SkPaint* paint) const
273     {
274         paint->setAntiAlias(m_state->useAA);
275         paint->setDither(true);
276         paint->setXfermodeMode(m_state->mode);
277         if (SkColorGetA(m_state->shadow.color) > 0) {
278
279             // Currently, only GraphicsContexts associated with the
280             // HTMLCanvasElement have shadows ignore transforms set.  This
281             // allows us to distinguish between CSS and Canvas shadows which
282             // have different rendering specifications.
283             SkScalar dy = m_state->shadow.dy;
284             uint32_t flags = SkBlurDrawLooper::kHighQuality_BlurFlag;
285             if (m_parentGfxCtx->shadowsIgnoreTransforms()) {
286                 dy = -dy;
287                 flags |= SkBlurDrawLooper::kIgnoreTransform_BlurFlag;
288                 flags |= SkBlurDrawLooper::kOverrideColor_BlurFlag;
289             }
290
291             SkDrawLooper* looper = new SkBlurDrawLooper(m_state->shadow.blur,
292                                                         m_state->shadow.dx,
293                                                         dy,
294                                                         m_state->shadow.color,
295                                                         flags);
296             paint->setLooper(looper)->unref();
297         }
298     }
299
300     void setupPaintFill(SkPaint* paint) const
301     {
302         this->setupPaintCommon(paint);
303         paint->setColor(m_state->applyAlpha(m_state->fillColor));
304     }
305
306     void setupPaintBitmap(SkPaint* paint) const
307     {
308         this->setupPaintCommon(paint);
309         // We only want the global alpha for bitmaps,
310         // so just give applyAlpha opaque black
311         paint->setColor(m_state->applyAlpha(0xFF000000));
312     }
313
314     // Sets up the paint for stroking. Returns true if the style is really
315     // just a dash of squares (the size of the paint's stroke-width.
316     bool setupPaintStroke(SkPaint* paint, SkRect* rect, bool isHLine = false)
317     {
318         this->setupPaintCommon(paint);
319         paint->setColor(m_state->applyAlpha(m_state->strokeColor));
320
321         float width = m_state->strokeThickness;
322
323         // This allows dashing and dotting to work properly for hairline strokes
324         // FIXME: Should we only do this for dashed and dotted strokes?
325         if (!width)
326             width = 1;
327
328         paint->setStyle(SkPaint::kStroke_Style);
329         paint->setStrokeWidth(SkFloatToScalar(width));
330         paint->setStrokeCap(m_state->lineCap);
331         paint->setStrokeJoin(m_state->lineJoin);
332         paint->setStrokeMiter(SkFloatToScalar(m_state->miterLimit));
333
334         if (rect && (RoundToInt(width) & 1))
335             rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
336
337         SkPathEffect* pe = m_state->pathEffect;
338         if (pe) {
339             paint->setPathEffect(pe);
340             return false;
341         }
342         switch (m_parentGfxCtx->strokeStyle()) {
343         case NoStroke:
344         case SolidStroke:
345             width = 0;
346             break;
347         case DashedStroke:
348             width = m_state->dashRatio * width;
349             break;
350             // No break
351         case DottedStroke:
352             break;
353         }
354
355         if (width > 0) {
356             // Return true if we're basically a dotted dash of squares
357             bool justSqrs = RoundToInt(width) == RoundToInt(paint->getStrokeWidth());
358
359             if (justSqrs || !isHLine || !setBitmapDash(paint, width)) {
360 #if 0
361                 // this is slow enough that we just skip it for now
362                 // see http://b/issue?id=4163023
363                 SkScalar intervals[] = { width, width };
364                 pe = new SkDashPathEffect(intervals, 2, 0);
365                 paint->setPathEffect(pe)->unref();
366 #endif
367             }
368             return justSqrs;
369         }
370         return false;
371     }
372
373     void clipPathAntiAliased(const SkPath& clipPath)
374     {
375         // If we are currently tracking any anti-alias clip paths, then we already
376         // have a layer in place and don't need to add another.
377         bool haveLayerOutstanding = m_state->antiAliasClipPaths.size();
378
379         // See comments in applyAntiAliasedClipPaths about how this works.
380         m_state->antiAliasClipPaths.append(clipPath);
381         if (!haveLayerOutstanding) {
382             SkRect bounds = clipPath.getBounds();
383             if (m_platformGfxCtx && m_platformGfxCtx->mCanvas) {
384                 m_platformGfxCtx->mCanvas->saveLayerAlpha(&bounds, 255,
385                     static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag
386                         | SkCanvas::kFullColorLayer_SaveFlag
387                         | SkCanvas::kClipToLayer_SaveFlag));
388                 m_platformGfxCtx->mCanvas->save();
389             } else
390                 ASSERT(0);
391         }
392     }
393
394     void applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths)
395     {
396         // Anti-aliased clipping:
397         //
398         // Refer to PlatformContextSkia.cpp's applyAntiAliasedClipPaths() for more details
399
400         if (m_platformGfxCtx && m_platformGfxCtx->mCanvas)
401             m_platformGfxCtx->mCanvas->restore();
402
403         SkPaint paint;
404         paint.setXfermodeMode(SkXfermode::kClear_Mode);
405         paint.setAntiAlias(true);
406         paint.setStyle(SkPaint::kFill_Style);
407
408         if (m_platformGfxCtx && m_platformGfxCtx->mCanvas)  {
409             for (size_t i = paths.size() - 1; i < paths.size(); --i) {
410                 paths[i].setFillType(SkPath::kInverseWinding_FillType);
411                 m_platformGfxCtx->mCanvas->drawPath(paths[i], paint);
412             }
413             m_platformGfxCtx->mCanvas->restore();
414         } else
415             ASSERT(0);
416     }
417
418     PlatformGraphicsContext* getPlatformGfxCtx()
419     {
420         return m_platformGfxCtx;
421     }
422
423     State* getState()
424     {
425         return m_state;
426     }
427 private:
428     State* m_state;
429     GraphicsContext* m_parentGfxCtx; // Back-ptr to our parent
430     PlatformGraphicsContext* m_platformGfxCtx;
431     SkDeque m_stateStack;
432     // Not supported yet
433     State& operator=(const State&);
434 };
435
436 static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm)
437 {
438     SkShader::TileMode mode = SkShader::kClamp_TileMode;
439
440     switch (sm) {
441     case SpreadMethodPad:
442         mode = SkShader::kClamp_TileMode;
443         break;
444     case SpreadMethodReflect:
445         mode = SkShader::kMirror_TileMode;
446         break;
447     case SpreadMethodRepeat:
448         mode = SkShader::kRepeat_TileMode;
449         break;
450     }
451     return mode;
452 }
453
454 static void extactShader(SkPaint* paint, Pattern* pat, Gradient* grad)
455 {
456     if (pat) {
457         // platformPattern() returns a cached obj
458         paint->setShader(pat->platformPattern(AffineTransform()));
459     } else if (grad) {
460         // grad->getShader() returns a cached obj
461         GradientSpreadMethod sm = grad->spreadMethod();
462         paint->setShader(grad->getShader(SpreadMethod2TileMode(sm)));
463     }
464 }
465
466 ////////////////////////////////////////////////////////////////////////////////////////////////
467
468 GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height)
469 {
470     PlatformGraphicsContext* pgc = new PlatformGraphicsContext();
471
472     SkBitmap bitmap;
473
474     bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
475     bitmap.allocPixels();
476     bitmap.eraseColor(0);
477     pgc->mCanvas->setBitmapDevice(bitmap);
478
479     GraphicsContext* ctx = new GraphicsContext(pgc);
480     return ctx;
481 }
482
483 ////////////////////////////////////////////////////////////////////////////////////////////////
484
485 void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
486 {
487     m_data = new GraphicsContextPlatformPrivate(this, gc);
488     setPaintingDisabled(!gc || !gc->mCanvas);
489 }
490
491 void GraphicsContext::platformDestroy()
492 {
493     delete m_data;
494 }
495
496 void GraphicsContext::savePlatformState()
497 {
498     // Save our private State
499     m_data->save();
500     // Save our native canvas
501     GC2CANVAS(this)->save();
502 }
503
504 void GraphicsContext::restorePlatformState()
505 {
506     // Restore our private State
507     m_data->restore();
508     // Restore our native canvas
509     GC2CANVAS(this)->restore();
510 }
511
512 bool GraphicsContext::willFill() const
513 {
514     return m_data->getState()->fillColor;
515 }
516
517 bool GraphicsContext::willStroke() const
518 {
519     return m_data->getState()->strokeColor;
520 }
521
522 // Draws a filled rectangle with a stroked border.
523 void GraphicsContext::drawRect(const IntRect& rect)
524 {
525     if (paintingDisabled())
526         return;
527
528     SkPaint paint;
529     SkRect r(rect);
530
531     if (fillColor().alpha()) {
532         m_data->setupPaintFill(&paint);
533         GC2CANVAS(this)->drawRect(r, paint);
534     }
535
536     // According to GraphicsContext.h, stroking inside drawRect always means
537     // a stroke of 1 inside the rect.
538     if (strokeStyle() != NoStroke && strokeColor().alpha()) {
539         paint.reset();
540         m_data->setupPaintStroke(&paint, &r);
541         paint.setPathEffect(0); // No dashing please
542         paint.setStrokeWidth(SK_Scalar1); // Always just 1.0 width
543         r.inset(SK_ScalarHalf, SK_ScalarHalf); // Ensure we're "inside"
544         GC2CANVAS(this)->drawRect(r, paint);
545     }
546 }
547
548 // This is only used to draw borders.
549 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
550 {
551     if (paintingDisabled())
552         return;
553
554     StrokeStyle style = strokeStyle();
555     if (style == NoStroke)
556         return;
557
558     SkPaint paint;
559     SkCanvas* canvas = GC2CANVAS(this);
560     const int idx = SkAbs32(point2.x() - point1.x());
561     const int idy = SkAbs32(point2.y() - point1.y());
562
563     // Special-case horizontal and vertical lines that are really just dots
564     if (m_data->setupPaintStroke(&paint, 0, !idy) && (!idx || !idy)) {
565         const SkScalar diameter = paint.getStrokeWidth();
566         const SkScalar radius = SkScalarHalf(diameter);
567         SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x()));
568         SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y()));
569         SkScalar dx, dy;
570         int count;
571         SkRect bounds;
572
573         if (!idy) { // Horizontal
574             bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius);
575             x += radius;
576             dx = diameter * 2;
577             dy = 0;
578             count = idx;
579         } else { // Vertical
580             bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy));
581             y += radius;
582             dx = 0;
583             dy = diameter * 2;
584             count = idy;
585         }
586
587         // The actual count is the number of ONs we hit alternating
588         // ON(diameter), OFF(diameter), ...
589         {
590             SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter);
591             // Now compute the number of cells (ON and OFF)
592             count = SkScalarRound(width);
593             // Now compute the number of ONs
594             count = (count + 1) >> 1;
595         }
596
597         SkAutoMalloc storage(count * sizeof(SkPoint));
598         SkPoint* verts = (SkPoint*)storage.get();
599         // Now build the array of vertices to past to drawPoints
600         for (int i = 0; i < count; i++) {
601             verts[i].set(x, y);
602             x += dx;
603             y += dy;
604         }
605
606         paint.setStyle(SkPaint::kFill_Style);
607         paint.setPathEffect(0);
608
609         // Clipping to bounds is not required for correctness, but it does
610         // allow us to reject the entire array of points if we are completely
611         // offscreen. This is common in a webpage for android, where most of
612         // the content is clipped out. If drawPoints took an (optional) bounds
613         // parameter, that might even be better, as we would *just* use it for
614         // culling, and not both wacking the canvas' save/restore stack.
615         canvas->save(SkCanvas::kClip_SaveFlag);
616         canvas->clipRect(bounds);
617         canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint);
618         canvas->restore();
619     } else {
620         SkPoint pts[2] = { point1, point2 };
621         canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
622     }
623 }
624
625 static void setrectForUnderline(SkRect* r, GraphicsContext* context, const FloatPoint& point, int yOffset, float width)
626 {
627     float lineThickness = context->strokeThickness();
628 #if 0
629     if (lineThickness < 1) // Do we really need/want this?
630         lineThickness = 1;
631 #endif
632     r->fLeft    = point.x();
633     r->fTop     = point.y() + yOffset;
634     r->fRight   = r->fLeft + width;
635     r->fBottom  = r->fTop + lineThickness;
636 }
637
638 void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool)
639 {
640     if (paintingDisabled())
641         return;
642
643     SkRect r;
644     setrectForUnderline(&r, this, pt, 0, width);
645
646     SkPaint paint;
647     paint.setAntiAlias(true);
648     paint.setColor(this->strokeColor().rgb());
649
650     GC2CANVAS(this)->drawRect(r, paint);
651 }
652
653 // TODO: Should we draw different based on TextCheckingLineStyle?
654 void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, TextCheckingLineStyle)
655 {
656     if (paintingDisabled())
657         return;
658
659     SkRect r;
660     setrectForUnderline(&r, this, pt, 0, width);
661
662     SkPaint paint;
663     paint.setAntiAlias(true);
664     paint.setColor(SK_ColorRED); // Is this specified somewhere?
665
666     GC2CANVAS(this)->drawRect(r, paint);
667 }
668
669 // This method is only used to draw the little circles used in lists.
670 void GraphicsContext::drawEllipse(const IntRect& rect)
671 {
672     if (paintingDisabled())
673         return;
674
675     SkPaint paint;
676     SkRect oval(rect);
677
678     if (fillColor().rgb() & 0xFF000000) {
679         m_data->setupPaintFill(&paint);
680         GC2CANVAS(this)->drawOval(oval, paint);
681     }
682     if (strokeStyle() != NoStroke) {
683         paint.reset();
684         m_data->setupPaintStroke(&paint, &oval);
685         GC2CANVAS(this)->drawOval(oval, paint);
686     }
687 }
688
689 static inline int fastMod(int value, int max)
690 {
691     int sign = SkExtractSign(value);
692
693     value = SkApplySign(value, sign);
694     if (value >= max)
695         value %= max;
696     return SkApplySign(value, sign);
697 }
698
699 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
700 {
701     if (paintingDisabled())
702         return;
703
704     SkPath path;
705     SkPaint paint;
706     SkRect oval(r);
707
708     if (strokeStyle() == NoStroke) {
709         m_data->setupPaintFill(&paint); // We want the fill color
710         paint.setStyle(SkPaint::kStroke_Style);
711         paint.setStrokeWidth(SkFloatToScalar(this->strokeThickness()));
712     } else
713         m_data->setupPaintStroke(&paint, 0);
714
715     // We do this before converting to scalar, so we don't overflow SkFixed
716     startAngle = fastMod(startAngle, 360);
717     angleSpan = fastMod(angleSpan, 360);
718
719     path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
720     GC2CANVAS(this)->drawPath(path, paint);
721 }
722
723 void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
724 {
725     if (paintingDisabled())
726         return;
727
728     if (numPoints <= 1)
729         return;
730
731     SkPaint paint;
732     SkPath path;
733
734     path.incReserve(numPoints);
735     path.moveTo(SkFloatToScalar(points[0].x()), SkFloatToScalar(points[0].y()));
736     for (size_t i = 1; i < numPoints; i++)
737         path.lineTo(SkFloatToScalar(points[i].x()), SkFloatToScalar(points[i].y()));
738
739     if (GC2CANVAS(this)->quickReject(path, shouldAntialias ?
740             SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) {
741         return;
742     }
743
744     if (fillColor().rgb() & 0xFF000000) {
745         m_data->setupPaintFill(&paint);
746         paint.setAntiAlias(shouldAntialias);
747         GC2CANVAS(this)->drawPath(path, paint);
748     }
749
750     if (strokeStyle() != NoStroke) {
751         paint.reset();
752         m_data->setupPaintStroke(&paint, 0);
753         paint.setAntiAlias(shouldAntialias);
754         GC2CANVAS(this)->drawPath(path, paint);
755     }
756 }
757
758 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
759                                       const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace)
760 {
761     if (paintingDisabled())
762         return;
763
764     SkPaint paint;
765     SkPath path;
766     SkScalar radii[8];
767
768     radii[0] = SkIntToScalar(topLeft.width());
769     radii[1] = SkIntToScalar(topLeft.height());
770     radii[2] = SkIntToScalar(topRight.width());
771     radii[3] = SkIntToScalar(topRight.height());
772     radii[4] = SkIntToScalar(bottomRight.width());
773     radii[5] = SkIntToScalar(bottomRight.height());
774     radii[6] = SkIntToScalar(bottomLeft.width());
775     radii[7] = SkIntToScalar(bottomLeft.height());
776     path.addRoundRect(rect, radii);
777
778     m_data->setupPaintFill(&paint);
779     paint.setColor(color.rgb());
780     GC2CANVAS(this)->drawPath(path, paint);
781 }
782
783 void GraphicsContext::fillRect(const FloatRect& rect)
784 {
785     save();
786     SkPaint paint;
787
788     m_data->setupPaintFill(&paint);
789
790     extactShader(&paint,
791                  m_state.fillPattern.get(),
792                  m_state.fillGradient.get());
793
794     GC2CANVAS(this)->drawRect(rect, paint);
795     restore();
796 }
797
798 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
799 {
800     if (paintingDisabled())
801         return;
802
803     if (color.rgb() & 0xFF000000) {
804         save();
805         SkPaint paint;
806
807         m_data->setupPaintCommon(&paint);
808         paint.setColor(color.rgb()); // Punch in the specified color
809         paint.setShader(0); // In case we had one set
810
811         // Sometimes we record and draw portions of the page, using clips
812         // for each portion. The problem with this is that webkit, sometimes,
813         // sees that we're only recording a portion, and they adjust some of
814         // their rectangle coordinates accordingly (e.g.
815         // RenderBoxModelObject::paintFillLayerExtended() which calls
816         // rect.intersect(paintInfo.rect) and then draws the bg with that
817         // rect. The result is that we end up drawing rects that are meant to
818         // seam together (one for each portion), but if the rects have
819         // fractional coordinates (e.g. we are zoomed by a fractional amount)
820         // we will double-draw those edges, resulting in visual cracks or
821         // artifacts.
822
823         // The fix seems to be to just turn off antialasing for rects (this
824         // entry-point in GraphicsContext seems to have been sufficient,
825         // though perhaps we'll find we need to do this as well in fillRect(r)
826         // as well.) Currently setupPaintCommon() enables antialiasing.
827
828         // Since we never show the page rotated at a funny angle, disabling
829         // antialiasing seems to have no real down-side, and it does fix the
830         // bug when we're zoomed (and drawing portions that need to seam).
831         paint.setAntiAlias(false);
832
833         GC2CANVAS(this)->drawRect(rect, paint);
834         restore();
835     }
836 }
837
838 void GraphicsContext::clip(const FloatRect& rect)
839 {
840     if (paintingDisabled())
841         return;
842
843     GC2CANVAS(this)->clipRect(rect);
844 }
845
846 void GraphicsContext::clip(const Path& path)
847 {
848     if (paintingDisabled())
849         return;
850
851     m_data->clipPathAntiAliased(*path.platformPath());
852 }
853
854 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
855 {
856     if (paintingDisabled())
857         return;
858
859     SkPath path;
860     SkRect r(rect);
861
862     path.addOval(r, SkPath::kCW_Direction);
863     // Only perform the inset if we won't invert r
864     if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
865         // Adding one to the thickness doesn't make the border too thick as
866         // it's painted over afterwards. But without this adjustment the
867         // border appears a little anemic after anti-aliasing.
868         r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1));
869         path.addOval(r, SkPath::kCCW_Direction);
870     }
871     m_data->clipPathAntiAliased(path);
872 }
873
874 void GraphicsContext::canvasClip(const Path& path)
875 {
876     clip(path);
877 }
878
879 void GraphicsContext::clipOut(const IntRect& r)
880 {
881     if (paintingDisabled())
882         return;
883
884     GC2CANVAS(this)->clipRect(r, SkRegion::kDifference_Op);
885 }
886
887 #if ENABLE(SVG)
888 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
889 {
890     if (paintingDisabled())
891         return;
892
893     SkPath path = *pathToClip.platformPath();
894     path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
895     GC2CANVAS(this)->clipPath(path);
896 }
897 #endif
898
899 void GraphicsContext::clipOut(const Path& p)
900 {
901     if (paintingDisabled())
902         return;
903
904     GC2CANVAS(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op);
905 }
906
907 //////////////////////////////////////////////////////////////////////////////////////////////////
908
909 #if SVG_SUPPORT
910 KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
911 {
912     return new KRenderingDeviceContextQuartz(platformContext());
913 }
914 #endif
915
916 // These are the flags we need when we call saveLayer for transparency.
917 // Since it does not appear that webkit intends this to also save/restore
918 // the matrix or clip, I do not give those flags (for performance)
919 #define TRANSPARENCY_SAVEFLAGS                                  \
920     (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag |   \
921                           SkCanvas::kFullColorLayer_SaveFlag)
922
923 void GraphicsContext::beginTransparencyLayer(float opacity)
924 {
925     if (paintingDisabled())
926         return;
927
928     SkCanvas* canvas = GC2CANVAS(this);
929     canvas->saveLayerAlpha(0, (int)(opacity * 255), TRANSPARENCY_SAVEFLAGS);
930 }
931
932 void GraphicsContext::endTransparencyLayer()
933 {
934     if (paintingDisabled())
935         return;
936
937     GC2CANVAS(this)->restore();
938 }
939
940 ///////////////////////////////////////////////////////////////////////////
941
942 void GraphicsContext::setupBitmapPaint(SkPaint* paint)
943 {
944     m_data->setupPaintBitmap(paint);
945 }
946
947 void GraphicsContext::setupFillPaint(SkPaint* paint)
948 {
949     m_data->setupPaintFill(paint);
950 }
951
952 void GraphicsContext::setupStrokePaint(SkPaint* paint)
953 {
954     m_data->setupPaintStroke(paint, 0);
955 }
956
957 bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset)
958 {
959     return m_data->getState()->setupShadowPaint(this, paint, offset);
960 }
961
962 void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace)
963 {
964     m_data->setStrokeColor(c);
965 }
966
967 void GraphicsContext::setPlatformStrokeThickness(float f)
968 {
969     m_data->setStrokeThickness(f);
970 }
971
972 void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace)
973 {
974     m_data->setFillColor(c);
975 }
976
977 void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
978 {
979     if (paintingDisabled())
980         return;
981
982     if (blur <= 0)
983         this->clearPlatformShadow();
984
985     SkColor c;
986     if (color.isValid())
987         c = color.rgb();
988     else
989         c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color
990     m_data->getState()->setShadow(blur, size.width(), size.height(), c);
991 }
992
993 void GraphicsContext::clearPlatformShadow()
994 {
995     if (paintingDisabled())
996         return;
997
998     m_data->getState()->setShadow(0, 0, 0, 0);
999 }
1000
1001 ///////////////////////////////////////////////////////////////////////////////
1002
1003 void GraphicsContext::drawFocusRing(const Vector<IntRect>&, int, int, const Color&)
1004 {
1005     // Do nothing, since we draw the focus ring independently.
1006 }
1007
1008 void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&)
1009 {
1010     // Do nothing, since we draw the focus ring independently.
1011 }
1012
1013 PlatformGraphicsContext* GraphicsContext::platformContext() const
1014 {
1015     ASSERT(!paintingDisabled());
1016     return m_data->getPlatformGfxCtx();
1017 }
1018
1019 void GraphicsContext::setMiterLimit(float limit)
1020 {
1021     m_data->getState()->miterLimit = limit;
1022 }
1023
1024 void GraphicsContext::setAlpha(float alpha)
1025 {
1026     m_data->getState()->alpha = alpha;
1027 }
1028
1029 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
1030 {
1031     m_data->getState()->mode = WebCoreCompositeToSkiaComposite(op);
1032 }
1033
1034 void GraphicsContext::clearRect(const FloatRect& rect)
1035 {
1036     if (paintingDisabled())
1037         return;
1038
1039     SkPaint paint;
1040
1041     m_data->setupPaintFill(&paint);
1042     paint.setXfermodeMode(SkXfermode::kClear_Mode);
1043     GC2CANVAS(this)->drawRect(rect, paint);
1044 }
1045
1046 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1047 {
1048     if (paintingDisabled())
1049         return;
1050
1051     SkPaint paint;
1052
1053     m_data->setupPaintStroke(&paint, 0);
1054     paint.setStrokeWidth(SkFloatToScalar(lineWidth));
1055     GC2CANVAS(this)->drawRect(rect, paint);
1056 }
1057
1058 void GraphicsContext::setLineCap(LineCap cap)
1059 {
1060     switch (cap) {
1061     case ButtCap:
1062         m_data->getState()->lineCap = SkPaint::kButt_Cap;
1063         break;
1064     case RoundCap:
1065         m_data->getState()->lineCap = SkPaint::kRound_Cap;
1066         break;
1067     case SquareCap:
1068         m_data->getState()->lineCap = SkPaint::kSquare_Cap;
1069         break;
1070     default:
1071         SkDEBUGF(("GraphicsContext::setLineCap: unknown LineCap %d\n", cap));
1072         break;
1073     }
1074 }
1075
1076 #if ENABLE(SVG)
1077 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
1078 {
1079     if (paintingDisabled())
1080         return;
1081
1082     size_t dashLength = dashes.size();
1083     if (!dashLength)
1084         return;
1085
1086     size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
1087     SkScalar* intervals = new SkScalar[count];
1088
1089     for (unsigned int i = 0; i < count; i++)
1090         intervals[i] = SkFloatToScalar(dashes[i % dashLength]);
1091     SkPathEffect **effectPtr = &m_data->getState()->pathEffect;
1092     SkSafeUnref(*effectPtr);
1093     *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset));
1094
1095     delete[] intervals;
1096 }
1097 #endif
1098
1099 void GraphicsContext::setLineJoin(LineJoin join)
1100 {
1101     switch (join) {
1102     case MiterJoin:
1103         m_data->getState()->lineJoin = SkPaint::kMiter_Join;
1104         break;
1105     case RoundJoin:
1106         m_data->getState()->lineJoin = SkPaint::kRound_Join;
1107         break;
1108     case BevelJoin:
1109         m_data->getState()->lineJoin = SkPaint::kBevel_Join;
1110         break;
1111     default:
1112         SkDEBUGF(("GraphicsContext::setLineJoin: unknown LineJoin %d\n", join));
1113         break;
1114     }
1115 }
1116
1117 void GraphicsContext::scale(const FloatSize& size)
1118 {
1119     if (paintingDisabled())
1120         return;
1121     GC2CANVAS(this)->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
1122 }
1123
1124 void GraphicsContext::rotate(float angleInRadians)
1125 {
1126     if (paintingDisabled())
1127         return;
1128     GC2CANVAS(this)->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f)));
1129 }
1130
1131 void GraphicsContext::translate(float x, float y)
1132 {
1133     if (paintingDisabled())
1134         return;
1135     GC2CANVAS(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y));
1136 }
1137
1138 void GraphicsContext::concatCTM(const AffineTransform& affine)
1139 {
1140     if (paintingDisabled())
1141         return;
1142     GC2CANVAS(this)->concat(affine);
1143 }
1144
1145 // This is intended to round the rect to device pixels (through the CTM)
1146 // and then invert the result back into source space, with the hope that when
1147 // it is drawn (through the matrix), it will land in the "right" place (i.e.
1148 // on pixel boundaries).
1149
1150 // For android, we record this geometry once and then draw it though various
1151 // scale factors as the user zooms, without re-recording. Thus this routine
1152 // should just leave the original geometry alone.
1153
1154 // If we instead draw into bitmap tiles, we should then perform this
1155 // transform -> round -> inverse step.
1156
1157 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
1158 {
1159     return rect;
1160 }
1161
1162 //////////////////////////////////////////////////////////////////////////////////////////////////
1163
1164 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1165 {
1166 // Appears to be PDF specific, so we ignore it
1167 #if 0
1168 if (paintingDisabled())
1169     return;
1170
1171 CFURLRef urlRef = link.createCFURL();
1172 if (urlRef) {
1173     CGContextRef context = platformContext();
1174
1175     // Get the bounding box to handle clipping.
1176     CGRect box = CGContextGetClipBoundingBox(context);
1177
1178     IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
1179     IntRect rect = destRect;
1180     rect.intersect(intBox);
1181
1182     CGPDFContextSetURLForRect(context, urlRef,
1183         CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
1184
1185     CFRelease(urlRef);
1186 }
1187 #endif
1188 }
1189
1190 void GraphicsContext::setPlatformShouldAntialias(bool useAA)
1191 {
1192     if (paintingDisabled())
1193         return;
1194     m_data->getState()->useAA = useAA;
1195 }
1196
1197 AffineTransform GraphicsContext::getCTM() const
1198 {
1199     const SkMatrix& m = GC2CANVAS(this)->getTotalMatrix();
1200     return AffineTransform(SkScalarToDouble(m.getScaleX()), // a
1201                            SkScalarToDouble(m.getSkewY()), // b
1202                            SkScalarToDouble(m.getSkewX()), // c
1203                            SkScalarToDouble(m.getScaleY()), // d
1204                            SkScalarToDouble(m.getTranslateX()), // e
1205                            SkScalarToDouble(m.getTranslateY())); // f
1206 }
1207
1208 void GraphicsContext::setCTM(const AffineTransform& transform)
1209 {
1210     if (paintingDisabled())
1211         return;
1212
1213     GC2CANVAS(this)->setMatrix(transform);
1214 }
1215
1216 ///////////////////////////////////////////////////////////////////////////////
1217
1218 void GraphicsContext::fillPath(const Path& pathToFill)
1219 {
1220     SkPath* path = pathToFill.platformPath();
1221     if (paintingDisabled() || !path)
1222         return;
1223
1224     switch (this->fillRule()) {
1225     case RULE_NONZERO:
1226         path->setFillType(SkPath::kWinding_FillType);
1227         break;
1228     case RULE_EVENODD:
1229         path->setFillType(SkPath::kEvenOdd_FillType);
1230         break;
1231     }
1232
1233     SkPaint paint;
1234     m_data->setupPaintFill(&paint);
1235
1236     extactShader(&paint,
1237                  m_state.fillPattern.get(),
1238                  m_state.fillGradient.get());
1239
1240     GC2CANVAS(this)->drawPath(*path, paint);
1241 }
1242
1243 void GraphicsContext::strokePath(const Path& pathToStroke)
1244 {
1245     const SkPath* path = pathToStroke.platformPath();
1246     if (paintingDisabled() || !path)
1247         return;
1248
1249     SkPaint paint;
1250     m_data->setupPaintStroke(&paint, 0);
1251
1252     extactShader(&paint,
1253                  m_state.strokePattern.get(),
1254                  m_state.strokeGradient.get());
1255
1256     GC2CANVAS(this)->drawPath(*path, paint);
1257 }
1258
1259 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1260 {
1261     notImplemented();
1262     return InterpolationDefault;
1263 }
1264
1265 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
1266 {
1267 #if 0
1268     enum InterpolationQuality {
1269         InterpolationDefault,
1270         InterpolationNone,
1271         InterpolationLow,
1272         InterpolationMedium,
1273         InterpolationHigh
1274     };
1275 #endif
1276     // TODO: record this, so we can know when to use bitmap-filtering when we draw
1277     // ... not sure how meaningful this will be given our playback model.
1278
1279     // Certainly safe to do nothing for the present.
1280 }
1281
1282 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias)
1283 {
1284     if (paintingDisabled())
1285         return;
1286
1287     if (numPoints <= 1)
1288         return;
1289
1290     // FIXME: IMPLEMENT!
1291 }
1292
1293 } // namespace WebCore
1294
1295 ///////////////////////////////////////////////////////////////////////////////
1296
1297 SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc)
1298 {
1299     return gc->platformContext()->mCanvas;
1300 }