• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 #include "config.h"
27 #include "GraphicsContext.h"
28 
29 #include "AffineTransform.h"
30 #include "Gradient.h"
31 #include "GraphicsContextPrivate.h"
32 #include "NotImplemented.h"
33 #include "Path.h"
34 #include "Pattern.h"
35 #include "PlatformGraphicsContext.h"
36 #include "SkBitmapRef.h"
37 #include "SkBlurDrawLooper.h"
38 #include "SkBlurMaskFilter.h"
39 #include "SkCanvas.h"
40 #include "SkColorPriv.h"
41 #include "SkDashPathEffect.h"
42 #include "SkDevice.h"
43 #include "SkGradientShader.h"
44 #include "SkPaint.h"
45 #include "SkString.h"
46 #include "SkiaUtils.h"
47 #include "TransformationMatrix.h"
48 #include "android_graphics.h"
49 
50 using namespace std;
51 
52 #define GC2Canvas(ctx)  (ctx)->m_data->mPgc->mCanvas
53 
54 namespace WebCore {
55 
RoundToInt(float x)56 static int RoundToInt(float x)
57 {
58     return (int)roundf(x);
59 }
60 
deepCopyPtr(const T * src)61 template <typename T> T* deepCopyPtr(const T* src) {
62     return src ? new T(*src) : NULL;
63 }
64 
65 /*  TODO / questions
66 
67     mAlpha: how does this interact with the alpha in Color? multiply them together?
68     mMode: do I always respect this? If so, then
69                      the rgb() & 0xFF000000 check will abort drawing too often
70     Is Color premultiplied or not? If it is, then I can't blindly pass it to paint.setColor()
71 */
72 
73 struct ShadowRec {
74     SkScalar    mBlur;  // >0 means valid shadow
75     SkScalar    mDx;
76     SkScalar    mDy;
77     SkColor     mColor;
78 };
79 
80 class GraphicsContextPlatformPrivate {
81 public:
82     GraphicsContext*             mCG;        // back-ptr to our parent
83     PlatformGraphicsContext*     mPgc;
84 
85     struct State {
86         SkPath*             mPath;
87         SkPathEffect* mPathEffect;
88         float               mMiterLimit;
89         float               mAlpha;
90         float               mStrokeThickness;
91         SkPaint::Cap        mLineCap;
92         SkPaint::Join       mLineJoin;
93         SkXfermode::Mode    mMode;
94         int                 mDashRatio; //ratio of the length of a dash to its width
95         ShadowRec           mShadow;
96         SkColor             mFillColor;
97         SkColor             mStrokeColor;
98         bool                mUseAA;
99 
StateWebCore::GraphicsContextPlatformPrivate::State100         State() {
101             mPath            = NULL;    // lazily allocated
102             mPathEffect = 0;
103             mMiterLimit      = 4;
104             mAlpha           = 1;
105             mStrokeThickness = 0.0f;  // Same as default in GraphicsContextPrivate.h
106             mLineCap         = SkPaint::kDefault_Cap;
107             mLineJoin        = SkPaint::kDefault_Join;
108             mMode            = SkXfermode::kSrcOver_Mode;
109             mDashRatio       = 3;
110             mUseAA           = true;
111             mShadow.mBlur    = 0;
112             mFillColor       = SK_ColorBLACK;
113             mStrokeColor     = SK_ColorBLACK;
114         }
115 
StateWebCore::GraphicsContextPlatformPrivate::State116         State(const State& other) {
117             memcpy(this, &other, sizeof(State));
118             mPath = deepCopyPtr<SkPath>(other.mPath);
119             mPathEffect->safeRef();
120         }
121 
~StateWebCore::GraphicsContextPlatformPrivate::State122         ~State() {
123             delete mPath;
124             mPathEffect->safeUnref();
125         }
126 
setShadowWebCore::GraphicsContextPlatformPrivate::State127         void setShadow(int radius, int dx, int dy, SkColor c) {
128             // cut the radius in half, to visually match the effect seen in
129             // safari browser
130             mShadow.mBlur = SkScalarHalf(SkIntToScalar(radius));
131             mShadow.mDx = SkIntToScalar(dx);
132             mShadow.mDy = SkIntToScalar(dy);
133             mShadow.mColor = c;
134         }
135 
setupShadowPaintWebCore::GraphicsContextPlatformPrivate::State136         bool setupShadowPaint(SkPaint* paint, SkPoint* offset) {
137             if (mShadow.mBlur > 0) {
138                 paint->setAntiAlias(true);
139                 paint->setDither(true);
140                 paint->setXfermodeMode(mMode);
141                 paint->setColor(mShadow.mColor);
142                 paint->setMaskFilter(SkBlurMaskFilter::Create(mShadow.mBlur,
143                                 SkBlurMaskFilter::kNormal_BlurStyle))->unref();
144                 offset->set(mShadow.mDx, mShadow.mDy);
145                 return true;
146             }
147             return false;
148         }
149 
applyAlphaWebCore::GraphicsContextPlatformPrivate::State150         SkColor applyAlpha(SkColor c) const
151         {
152             int s = RoundToInt(mAlpha * 256);
153             if (s >= 256)
154                 return c;
155             if (s < 0)
156                 return 0;
157 
158             int a = SkAlphaMul(SkColorGetA(c), s);
159             return (c & 0x00FFFFFF) | (a << 24);
160         }
161     };
162 
163     SkDeque mStateStack;
164     State*  mState;
165 
GraphicsContextPlatformPrivate(GraphicsContext * cg,PlatformGraphicsContext * pgc)166     GraphicsContextPlatformPrivate(GraphicsContext* cg, PlatformGraphicsContext* pgc)
167             : mCG(cg)
168             , mPgc(pgc), mStateStack(sizeof(State)) {
169         State* state = (State*)mStateStack.push_back();
170         new (state) State();
171         mState = state;
172     }
173 
~GraphicsContextPlatformPrivate()174     ~GraphicsContextPlatformPrivate() {
175         if (mPgc && mPgc->deleteUs())
176             delete mPgc;
177 
178         // we force restores so we don't leak any subobjects owned by our
179         // stack of State records.
180         while (mStateStack.count() > 0)
181             this->restore();
182     }
183 
save()184     void save() {
185         State* newState = (State*)mStateStack.push_back();
186         new (newState) State(*mState);
187         mState = newState;
188     }
189 
restore()190     void restore() {
191         mState->~State();
192         mStateStack.pop_back();
193         mState = (State*)mStateStack.back();
194     }
195 
setFillColor(const Color & c)196     void setFillColor(const Color& c) {
197         mState->mFillColor = c.rgb();
198     }
199 
setStrokeColor(const Color & c)200     void setStrokeColor(const Color& c) {
201         mState->mStrokeColor = c.rgb();
202     }
203 
setStrokeThickness(float f)204     void setStrokeThickness(float f) {
205         mState->mStrokeThickness = f;
206     }
207 
beginPath()208     void beginPath() {
209         if (mState->mPath) {
210             mState->mPath->reset();
211         }
212     }
213 
addPath(const SkPath & other)214     void addPath(const SkPath& other) {
215         if (!mState->mPath) {
216             mState->mPath = new SkPath(other);
217         } else {
218             mState->mPath->addPath(other);
219         }
220     }
221 
222     // may return null
getPath() const223     SkPath* getPath() const { return mState->mPath; }
224 
setup_paint_common(SkPaint * paint) const225     void setup_paint_common(SkPaint* paint) const {
226         paint->setAntiAlias(mState->mUseAA);
227         paint->setDither(true);
228         paint->setXfermodeMode(mState->mMode);
229         if (mState->mShadow.mBlur > 0) {
230             SkDrawLooper* looper = new SkBlurDrawLooper(mState->mShadow.mBlur,
231                                                         mState->mShadow.mDx,
232                                                         mState->mShadow.mDy,
233                                                         mState->mShadow.mColor);
234             paint->setLooper(looper)->unref();
235         }
236 
237         /* need to sniff textDrawingMode(), which returns the bit_OR of...
238              const int cTextInvisible = 0;
239              const int cTextFill = 1;
240              const int cTextStroke = 2;
241              const int cTextClip = 4;
242          */
243     }
244 
setup_paint_fill(SkPaint * paint) const245     void setup_paint_fill(SkPaint* paint) const {
246         this->setup_paint_common(paint);
247         paint->setColor(mState->applyAlpha(mState->mFillColor));
248     }
249 
setup_paint_bitmap(SkPaint * paint) const250     void setup_paint_bitmap(SkPaint* paint) const {
251         this->setup_paint_common(paint);
252         // we only want the global alpha for bitmaps,
253         // so just give applyAlpha opaque black
254         paint->setColor(mState->applyAlpha(0xFF000000));
255     }
256 
257     /*  sets up the paint for stroking. Returns true if the style is really
258         just a dash of squares (the size of the paint's stroke-width.
259     */
setup_paint_stroke(SkPaint * paint,SkRect * rect)260     bool setup_paint_stroke(SkPaint* paint, SkRect* rect) {
261         this->setup_paint_common(paint);
262         paint->setColor(mState->applyAlpha(mState->mStrokeColor));
263 
264         float width = mState->mStrokeThickness;
265 
266         // this allows dashing and dotting to work properly for hairline strokes
267         // FIXME: Should we only do this for dashed and dotted strokes?
268         if (0 == width) {
269             width = 1;
270         }
271 
272 //        paint->setColor(mState->applyAlpha(mCG->strokeColor().rgb()));
273         paint->setStyle(SkPaint::kStroke_Style);
274         paint->setStrokeWidth(SkFloatToScalar(width));
275         paint->setStrokeCap(mState->mLineCap);
276         paint->setStrokeJoin(mState->mLineJoin);
277         paint->setStrokeMiter(SkFloatToScalar(mState->mMiterLimit));
278 
279         if (rect != NULL && (RoundToInt(width) & 1)) {
280             rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
281         }
282 
283         SkPathEffect* pe = mState->mPathEffect;
284         if (pe) {
285             paint->setPathEffect(pe);
286             return false;
287         }
288         switch (mCG->strokeStyle()) {
289             case NoStroke:
290             case SolidStroke:
291                 width = 0;
292                 break;
293             case DashedStroke:
294                 width = mState->mDashRatio * width;
295                 break;
296                 /* no break */
297             case DottedStroke:
298                 break;
299         }
300 
301         if (width > 0) {
302             SkScalar intervals[] = { width, width };
303             pe = new SkDashPathEffect(intervals, 2, 0);
304             paint->setPathEffect(pe)->unref();
305             // return true if we're basically a dotted dash of squares
306             return RoundToInt(width) == RoundToInt(paint->getStrokeWidth());
307         }
308         return false;
309     }
310 
311 private:
312     // not supported yet
313     State& operator=(const State&);
314 };
315 
SpreadMethod2TileMode(GradientSpreadMethod sm)316 static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm) {
317     SkShader::TileMode mode = SkShader::kClamp_TileMode;
318 
319     switch (sm) {
320         case SpreadMethodPad:
321             mode = SkShader::kClamp_TileMode;
322             break;
323         case SpreadMethodReflect:
324             mode = SkShader::kMirror_TileMode;
325             break;
326         case SpreadMethodRepeat:
327             mode = SkShader::kRepeat_TileMode;
328             break;
329     }
330     return mode;
331 }
332 
extactShader(SkPaint * paint,Pattern * pat,Gradient * grad)333 static void extactShader(SkPaint* paint, Pattern* pat, Gradient* grad)
334 {
335     if (pat) {
336         // platformPattern() returns a cached obj
337         paint->setShader(pat->platformPattern(AffineTransform()));
338     } else if (grad) {
339         // grad->getShader() returns a cached obj
340         GradientSpreadMethod sm = grad->spreadMethod();
341         paint->setShader(grad->getShader(SpreadMethod2TileMode(sm)));
342     }
343 }
344 
345 ////////////////////////////////////////////////////////////////////////////////////////////////
346 
createOffscreenContext(int width,int height)347 GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height)
348 {
349     PlatformGraphicsContext* pgc = new PlatformGraphicsContext();
350 
351     SkBitmap    bitmap;
352 
353     bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
354     bitmap.allocPixels();
355     bitmap.eraseColor(0);
356     pgc->mCanvas->setBitmapDevice(bitmap);
357 
358     GraphicsContext* ctx = new GraphicsContext(pgc);
359 //printf("-------- create offscreen <canvas> %p\n", ctx);
360     return ctx;
361 }
362 
363 ////////////////////////////////////////////////////////////////////////////////////////////////
364 
GraphicsContext(PlatformGraphicsContext * gc)365 GraphicsContext::GraphicsContext(PlatformGraphicsContext *gc)
366     : m_common(createGraphicsContextPrivate())
367     , m_data(new GraphicsContextPlatformPrivate(this, gc))
368 {
369     setPaintingDisabled(NULL == gc || NULL == gc->mCanvas);
370 }
371 
~GraphicsContext()372 GraphicsContext::~GraphicsContext()
373 {
374     delete m_data;
375     this->destroyGraphicsContextPrivate(m_common);
376 }
377 
savePlatformState()378 void GraphicsContext::savePlatformState()
379 {
380     // save our private State
381     m_data->save();
382     // save our native canvas
383     GC2Canvas(this)->save();
384 }
385 
restorePlatformState()386 void GraphicsContext::restorePlatformState()
387 {
388     // restore our native canvas
389     GC2Canvas(this)->restore();
390     // restore our private State
391     m_data->restore();
392 }
393 
willFill() const394 bool GraphicsContext::willFill() const {
395     return m_data->mState->mFillColor != 0;
396 }
397 
willStroke() const398 bool GraphicsContext::willStroke() const {
399     return m_data->mState->mStrokeColor != 0;
400 }
401 
getCurrPath() const402 const SkPath* GraphicsContext::getCurrPath() const {
403     return m_data->mState->mPath;
404 }
405 
406 // Draws a filled rectangle with a stroked border.
drawRect(const IntRect & rect)407 void GraphicsContext::drawRect(const IntRect& rect)
408 {
409     if (paintingDisabled())
410         return;
411 
412     SkPaint paint;
413     SkRect  r(rect);
414 
415     if (fillColor().alpha()) {
416         m_data->setup_paint_fill(&paint);
417         GC2Canvas(this)->drawRect(r, paint);
418     }
419 
420     /*  According to GraphicsContext.h, stroking inside drawRect always means
421         a stroke of 1 inside the rect.
422      */
423     if (strokeStyle() != NoStroke && strokeColor().alpha()) {
424         paint.reset();
425         m_data->setup_paint_stroke(&paint, &r);
426         paint.setPathEffect(NULL);              // no dashing please
427         paint.setStrokeWidth(SK_Scalar1);       // always just 1.0 width
428         r.inset(SK_ScalarHalf, SK_ScalarHalf);  // ensure we're "inside"
429         GC2Canvas(this)->drawRect(r, paint);
430     }
431 }
432 
433 // This is only used to draw borders.
drawLine(const IntPoint & point1,const IntPoint & point2)434 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
435 {
436     if (paintingDisabled())
437         return;
438 
439     StrokeStyle style = strokeStyle();
440     if (style == NoStroke)
441         return;
442 
443     SkPaint paint;
444     SkCanvas* canvas = GC2Canvas(this);
445     const int idx = SkAbs32(point2.x() - point1.x());
446     const int idy = SkAbs32(point2.y() - point1.y());
447 
448     // special-case horizontal and vertical lines that are really just dots
449     if (m_data->setup_paint_stroke(&paint, NULL) && (0 == idx || 0 == idy)) {
450         const SkScalar diameter = paint.getStrokeWidth();
451         const SkScalar radius = SkScalarHalf(diameter);
452         SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x()));
453         SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y()));
454         SkScalar dx, dy;
455         int count;
456         SkRect bounds;
457 
458         if (0 == idy) { // horizontal
459             bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius);
460             x += radius;
461             dx = diameter * 2;
462             dy = 0;
463             count = idx;
464         } else {        // vertical
465             bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy));
466             y += radius;
467             dx = 0;
468             dy = diameter * 2;
469             count = idy;
470         }
471 
472         // the actual count is the number of ONs we hit alternating
473         // ON(diameter), OFF(diameter), ...
474         {
475             SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter);
476             // now computer the number of cells (ON and OFF)
477             count = SkScalarRound(width);
478             // now compute the number of ONs
479             count = (count + 1) >> 1;
480         }
481 
482         SkAutoMalloc storage(count * sizeof(SkPoint));
483         SkPoint* verts = (SkPoint*)storage.get();
484         // now build the array of vertices to past to drawPoints
485         for (int i = 0; i < count; i++) {
486             verts[i].set(x, y);
487             x += dx;
488             y += dy;
489         }
490 
491         paint.setStyle(SkPaint::kFill_Style);
492         paint.setPathEffect(NULL);
493 
494         //  clipping to bounds is not required for correctness, but it does
495         //  allow us to reject the entire array of points if we are completely
496         //  offscreen. This is common in a webpage for android, where most of
497         //  the content is clipped out. If drawPoints took an (optional) bounds
498         //  parameter, that might even be better, as we would *just* use it for
499         //  culling, and not both wacking the canvas' save/restore stack.
500         canvas->save(SkCanvas::kClip_SaveFlag);
501         canvas->clipRect(bounds);
502         canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint);
503         canvas->restore();
504     } else {
505         SkPoint pts[2] = { point1, point2 };
506         canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
507     }
508 }
509 
setrect_for_underline(SkRect * r,GraphicsContext * context,const IntPoint & point,int yOffset,int width)510 static void setrect_for_underline(SkRect* r, GraphicsContext* context, const IntPoint& point, int yOffset, int width)
511 {
512     float lineThickness = context->strokeThickness();
513 //    if (lineThickness < 1)  // do we really need/want this?
514 //        lineThickness = 1;
515 
516     yOffset += 1;   // we add 1 to have underlines appear below the text
517 
518     r->fLeft    = SkIntToScalar(point.x());
519     r->fTop     = SkIntToScalar(point.y() + yOffset);
520     r->fRight   = r->fLeft + SkIntToScalar(width);
521     r->fBottom  = r->fTop + SkFloatToScalar(lineThickness);
522 }
523 
drawLineForText(IntPoint const & pt,int width,bool)524 void GraphicsContext::drawLineForText(IntPoint const& pt, int width, bool)
525 {
526     if (paintingDisabled())
527         return;
528 
529     SkRect  r;
530     setrect_for_underline(&r, this, pt, 0, width);
531 
532     SkPaint paint;
533     paint.setAntiAlias(true);
534     paint.setColor(this->strokeColor().rgb());
535 
536     GC2Canvas(this)->drawRect(r, paint);
537 }
538 
drawLineForMisspellingOrBadGrammar(const IntPoint & pt,int width,bool grammar)539 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& pt, int width, bool grammar)
540 {
541     if (paintingDisabled())
542         return;
543 
544     SkRect  r;
545     setrect_for_underline(&r, this, pt, 0, width);
546 
547     SkPaint paint;
548     paint.setAntiAlias(true);
549     paint.setColor(SK_ColorRED);    // is this specified somewhere?
550 
551     GC2Canvas(this)->drawRect(r, paint);
552 }
553 
554 // This method is only used to draw the little circles used in lists.
drawEllipse(const IntRect & rect)555 void GraphicsContext::drawEllipse(const IntRect& rect)
556 {
557     if (paintingDisabled())
558         return;
559 
560     SkPaint paint;
561     SkRect  oval(rect);
562 
563     if (fillColor().rgb() & 0xFF000000) {
564         m_data->setup_paint_fill(&paint);
565         GC2Canvas(this)->drawOval(oval, paint);
566     }
567     if (strokeStyle() != NoStroke) {
568         paint.reset();
569         m_data->setup_paint_stroke(&paint, &oval);
570         GC2Canvas(this)->drawOval(oval, paint);
571     }
572 }
573 
fast_mod(int value,int max)574 static inline int fast_mod(int value, int max)
575 {
576     int sign = SkExtractSign(value);
577 
578     value = SkApplySign(value, sign);
579     if (value >= max) {
580         value %= max;
581     }
582     return SkApplySign(value, sign);
583 }
584 
strokeArc(const IntRect & r,int startAngle,int angleSpan)585 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
586 {
587     if (paintingDisabled())
588         return;
589 
590     SkPath  path;
591     SkPaint paint;
592     SkRect  oval(r);
593 
594     if (strokeStyle() == NoStroke) {
595         m_data->setup_paint_fill(&paint);   // we want the fill color
596         paint.setStyle(SkPaint::kStroke_Style);
597         paint.setStrokeWidth(SkFloatToScalar(this->strokeThickness()));
598     }
599     else {
600         m_data->setup_paint_stroke(&paint, NULL);
601     }
602 
603     // we do this before converting to scalar, so we don't overflow SkFixed
604     startAngle = fast_mod(startAngle, 360);
605     angleSpan = fast_mod(angleSpan, 360);
606 
607     path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
608     GC2Canvas(this)->drawPath(path, paint);
609 }
610 
drawConvexPolygon(size_t numPoints,const FloatPoint * points,bool shouldAntialias)611 void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
612 {
613     if (paintingDisabled())
614         return;
615 
616     if (numPoints <= 1)
617         return;
618 
619     SkPaint paint;
620     SkPath  path;
621 
622     path.incReserve(numPoints);
623     path.moveTo(SkFloatToScalar(points[0].x()), SkFloatToScalar(points[0].y()));
624     for (size_t i = 1; i < numPoints; i++)
625         path.lineTo(SkFloatToScalar(points[i].x()), SkFloatToScalar(points[i].y()));
626 
627     if (GC2Canvas(this)->quickReject(path, shouldAntialias ?
628             SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) {
629         return;
630     }
631 
632     if (fillColor().rgb() & 0xFF000000) {
633         m_data->setup_paint_fill(&paint);
634         paint.setAntiAlias(shouldAntialias);
635         GC2Canvas(this)->drawPath(path, paint);
636     }
637 
638     if (strokeStyle() != NoStroke) {
639         paint.reset();
640         m_data->setup_paint_stroke(&paint, NULL);
641         paint.setAntiAlias(shouldAntialias);
642         GC2Canvas(this)->drawPath(path, paint);
643     }
644 }
645 
fillRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight,const Color & color,ColorSpace)646 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
647                                       const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace)
648 {
649     if (paintingDisabled())
650         return;
651 
652     SkPaint paint;
653     SkPath  path;
654     SkScalar radii[8];
655 
656     radii[0] = SkIntToScalar(topLeft.width());
657     radii[1] = SkIntToScalar(topLeft.height());
658     radii[2] = SkIntToScalar(topRight.width());
659     radii[3] = SkIntToScalar(topRight.height());
660     radii[4] = SkIntToScalar(bottomRight.width());
661     radii[5] = SkIntToScalar(bottomRight.height());
662     radii[6] = SkIntToScalar(bottomLeft.width());
663     radii[7] = SkIntToScalar(bottomLeft.height());
664     path.addRoundRect(rect, radii);
665 
666     m_data->setup_paint_fill(&paint);
667     GC2Canvas(this)->drawPath(path, paint);
668 }
669 
fillRect(const FloatRect & rect)670 void GraphicsContext::fillRect(const FloatRect& rect)
671 {
672     SkPaint paint;
673 
674     m_data->setup_paint_fill(&paint);
675 
676     extactShader(&paint,
677                  m_common->state.fillPattern.get(),
678                  m_common->state.fillGradient.get());
679 
680     GC2Canvas(this)->drawRect(rect, paint);
681 }
682 
fillRect(const FloatRect & rect,const Color & color,ColorSpace)683 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
684 {
685     if (paintingDisabled())
686         return;
687 
688     if (color.rgb() & 0xFF000000) {
689         SkPaint paint;
690 
691         m_data->setup_paint_common(&paint);
692         paint.setColor(color.rgb());    // punch in the specified color
693         paint.setShader(NULL);          // in case we had one set
694 
695         /*  Sometimes we record and draw portions of the page, using clips
696             for each portion. The problem with this is that webkit, sometimes,
697             sees that we're only recording a portion, and they adjust some of
698             their rectangle coordinates accordingly (e.g.
699             RenderBoxModelObject::paintFillLayerExtended() which calls
700             rect.intersect(paintInfo.rect) and then draws the bg with that
701             rect. The result is that we end up drawing rects that are meant to
702             seam together (one for each portion), but if the rects have
703             fractional coordinates (e.g. we are zoomed by a fractional amount)
704             we will double-draw those edges, resulting in visual cracks or
705             artifacts.
706 
707             The fix seems to be to just turn off antialasing for rects (this
708             entry-point in GraphicsContext seems to have been sufficient,
709             though perhaps we'll find we need to do this as well in fillRect(r)
710             as well.) Currently setup_paint_common() enables antialiasing.
711 
712             Since we never show the page rotated at a funny angle, disabling
713             antialiasing seems to have no real down-side, and it does fix the
714             bug when we're zoomed (and drawing portions that need to seam).
715          */
716         paint.setAntiAlias(false);
717 
718         GC2Canvas(this)->drawRect(rect, paint);
719     }
720 }
721 
clip(const FloatRect & rect)722 void GraphicsContext::clip(const FloatRect& rect)
723 {
724     if (paintingDisabled())
725         return;
726 
727     GC2Canvas(this)->clipRect(rect);
728 }
729 
clip(const Path & path)730 void GraphicsContext::clip(const Path& path)
731 {
732     if (paintingDisabled())
733         return;
734 
735 //    path.platformPath()->dump(false, "clip path");
736 
737     GC2Canvas(this)->clipPath(*path.platformPath());
738 }
739 
addInnerRoundedRectClip(const IntRect & rect,int thickness)740 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
741 {
742     if (paintingDisabled())
743         return;
744 
745 //printf("-------- addInnerRoundedRectClip: [%d %d %d %d] thickness=%d\n", rect.x(), rect.y(), rect.width(), rect.height(), thickness);
746 
747     SkPath  path;
748     SkRect r(rect);
749 
750     path.addOval(r, SkPath::kCW_Direction);
751     // only perform the inset if we won't invert r
752     if (2*thickness < rect.width() && 2*thickness < rect.height())
753     {
754         r.inset(SkIntToScalar(thickness) ,SkIntToScalar(thickness));
755         path.addOval(r, SkPath::kCCW_Direction);
756     }
757     GC2Canvas(this)->clipPath(path);
758 }
759 
canvasClip(const Path & path)760 void GraphicsContext::canvasClip(const Path& path)
761 {
762     clip(path);
763 }
764 
clipOut(const IntRect & r)765 void GraphicsContext::clipOut(const IntRect& r)
766 {
767     if (paintingDisabled())
768         return;
769 
770     GC2Canvas(this)->clipRect(r, SkRegion::kDifference_Op);
771 }
772 
clipOutEllipseInRect(const IntRect & r)773 void GraphicsContext::clipOutEllipseInRect(const IntRect& r)
774 {
775     if (paintingDisabled())
776         return;
777 
778     SkPath path;
779 
780     path.addOval(r, SkPath::kCCW_Direction);
781     GC2Canvas(this)->clipPath(path, SkRegion::kDifference_Op);
782 }
783 
784 #if ENABLE(SVG)
clipPath(WindRule clipRule)785 void GraphicsContext::clipPath(WindRule clipRule)
786 {
787     if (paintingDisabled())
788         return;
789     const SkPath* oldPath = m_data->getPath();
790     SkPath path(*oldPath);
791     path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
792     GC2Canvas(this)->clipPath(path);
793 }
794 #endif
795 
clipOut(const Path & p)796 void GraphicsContext::clipOut(const Path& p)
797 {
798     if (paintingDisabled())
799         return;
800 
801     GC2Canvas(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op);
802 }
803 
clipToImageBuffer(const FloatRect &,const ImageBuffer *)804 void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) {
805     SkDebugf("xxxxxxxxxxxxxxxxxx clipToImageBuffer not implemented\n");
806 }
807 
808 //////////////////////////////////////////////////////////////////////////////////////////////////
809 
810 #if SVG_SUPPORT
createRenderingDeviceContext()811 KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
812 {
813     return new KRenderingDeviceContextQuartz(platformContext());
814 }
815 #endif
816 
817 /*  These are the flags we need when we call saveLayer for transparency.
818     Since it does not appear that webkit intends this to also save/restore
819     the matrix or clip, I do not give those flags (for performance)
820  */
821 #define TRANSPARENCY_SAVEFLAGS                                  \
822     (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag |   \
823                           SkCanvas::kFullColorLayer_SaveFlag)
824 
beginTransparencyLayer(float opacity)825 void GraphicsContext::beginTransparencyLayer(float opacity)
826 {
827     if (paintingDisabled())
828         return;
829 
830     SkCanvas* canvas = GC2Canvas(this);
831     canvas->saveLayerAlpha(NULL, (int)(opacity * 255), TRANSPARENCY_SAVEFLAGS);
832 }
833 
endTransparencyLayer()834 void GraphicsContext::endTransparencyLayer()
835 {
836     if (paintingDisabled())
837         return;
838 
839     GC2Canvas(this)->restore();
840 }
841 
842     ///////////////////////////////////////////////////////////////////////////
843 
setupBitmapPaint(SkPaint * paint)844     void GraphicsContext::setupBitmapPaint(SkPaint* paint) {
845         m_data->setup_paint_bitmap(paint);
846     }
847 
setupFillPaint(SkPaint * paint)848     void GraphicsContext::setupFillPaint(SkPaint* paint) {
849         m_data->setup_paint_fill(paint);
850     }
851 
setupStrokePaint(SkPaint * paint)852     void GraphicsContext::setupStrokePaint(SkPaint* paint) {
853         m_data->setup_paint_stroke(paint, NULL);
854     }
855 
setupShadowPaint(SkPaint * paint,SkPoint * offset)856     bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset) {
857         return m_data->mState->setupShadowPaint(paint, offset);
858     }
859 
setPlatformStrokeColor(const Color & c,ColorSpace)860     void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace) {
861         m_data->setStrokeColor(c);
862     }
863 
setPlatformStrokeThickness(float f)864     void GraphicsContext::setPlatformStrokeThickness(float f) {
865         m_data->setStrokeThickness(f);
866     }
867 
setPlatformFillColor(const Color & c,ColorSpace)868     void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace) {
869         m_data->setFillColor(c);
870     }
871 
setPlatformShadow(const IntSize & size,int blur,const Color & color,ColorSpace)872 void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Color& color, ColorSpace)
873 {
874     if (paintingDisabled())
875         return;
876 
877     if (blur <= 0) {
878         this->clearPlatformShadow();
879     }
880 
881     SkColor c;
882     if (color.isValid()) {
883         c = color.rgb();
884     } else {
885         c = SkColorSetARGB(0xFF/3, 0, 0, 0);    // "std" Apple shadow color
886     }
887     m_data->mState->setShadow(blur, size.width(), size.height(), c);
888 }
889 
clearPlatformShadow()890 void GraphicsContext::clearPlatformShadow()
891 {
892     if (paintingDisabled())
893         return;
894 
895     m_data->mState->setShadow(0, 0, 0, 0);
896 }
897 
898 ///////////////////////////////////////////////////////////////////////////////
899 
drawFocusRing(const Vector<IntRect> &,int,int,const Color &)900 void GraphicsContext::drawFocusRing(const Vector<IntRect>&, int, int, const Color&)
901 {
902     // Do nothing, since we draw the focus ring independently.
903 }
904 
drawFocusRing(const Vector<Path> &,int,int,const Color &)905 void GraphicsContext::drawFocusRing(const Vector<Path>&, int, int, const Color&)
906 {
907     // Do nothing, since we draw the focus ring independently.
908 }
909 
platformContext() const910 PlatformGraphicsContext* GraphicsContext::platformContext() const
911 {
912     ASSERT(!paintingDisabled());
913     return m_data->mPgc;
914 }
915 
setMiterLimit(float limit)916 void GraphicsContext::setMiterLimit(float limit)
917 {
918     m_data->mState->mMiterLimit = limit;
919 }
920 
setAlpha(float alpha)921 void GraphicsContext::setAlpha(float alpha)
922 {
923     m_data->mState->mAlpha = alpha;
924 }
925 
setCompositeOperation(CompositeOperator op)926 void GraphicsContext::setCompositeOperation(CompositeOperator op)
927 {
928     m_data->mState->mMode = WebCoreCompositeToSkiaComposite(op);
929 }
930 
clearRect(const FloatRect & rect)931 void GraphicsContext::clearRect(const FloatRect& rect)
932 {
933     if (paintingDisabled())
934         return;
935 
936     SkPaint paint;
937 
938     m_data->setup_paint_fill(&paint);
939     paint.setXfermodeMode(SkXfermode::kClear_Mode);
940     GC2Canvas(this)->drawRect(rect, paint);
941 }
942 
strokeRect(const FloatRect & rect,float lineWidth)943 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
944 {
945     if (paintingDisabled())
946         return;
947 
948     SkPaint paint;
949 
950     m_data->setup_paint_stroke(&paint, NULL);
951     paint.setStrokeWidth(SkFloatToScalar(lineWidth));
952     GC2Canvas(this)->drawRect(rect, paint);
953 }
954 
setLineCap(LineCap cap)955 void GraphicsContext::setLineCap(LineCap cap)
956 {
957     switch (cap) {
958     case ButtCap:
959         m_data->mState->mLineCap = SkPaint::kButt_Cap;
960         break;
961     case RoundCap:
962         m_data->mState->mLineCap = SkPaint::kRound_Cap;
963         break;
964     case SquareCap:
965         m_data->mState->mLineCap = SkPaint::kSquare_Cap;
966         break;
967     default:
968         SkDEBUGF(("GraphicsContext::setLineCap: unknown LineCap %d\n", cap));
969         break;
970     }
971 }
972 
973 #if ENABLE(SVG)
setLineDash(const DashArray & dashes,float dashOffset)974 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
975 {
976     if (paintingDisabled())
977         return;
978 
979     size_t dashLength = dashes.size();
980     if (!dashLength)
981         return;
982 
983     size_t count = (dashLength % 2) == 0 ? dashLength : dashLength * 2;
984     SkScalar* intervals = new SkScalar[count];
985 
986     for (unsigned int i = 0; i < count; i++)
987         intervals[i] = SkFloatToScalar(dashes[i % dashLength]);
988     SkPathEffect **effectPtr = &m_data->mState->mPathEffect;
989     (*effectPtr)->safeUnref();
990     *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset));
991 
992     delete[] intervals;
993 }
994 #endif
995 
setLineJoin(LineJoin join)996 void GraphicsContext::setLineJoin(LineJoin join)
997 {
998     switch (join) {
999     case MiterJoin:
1000         m_data->mState->mLineJoin = SkPaint::kMiter_Join;
1001         break;
1002     case RoundJoin:
1003         m_data->mState->mLineJoin = SkPaint::kRound_Join;
1004         break;
1005     case BevelJoin:
1006         m_data->mState->mLineJoin = SkPaint::kBevel_Join;
1007         break;
1008     default:
1009         SkDEBUGF(("GraphicsContext::setLineJoin: unknown LineJoin %d\n", join));
1010         break;
1011     }
1012 }
1013 
scale(const FloatSize & size)1014 void GraphicsContext::scale(const FloatSize& size)
1015 {
1016     if (paintingDisabled())
1017         return;
1018     GC2Canvas(this)->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
1019 }
1020 
rotate(float angleInRadians)1021 void GraphicsContext::rotate(float angleInRadians)
1022 {
1023     if (paintingDisabled())
1024         return;
1025     GC2Canvas(this)->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f)));
1026 }
1027 
translate(float x,float y)1028 void GraphicsContext::translate(float x, float y)
1029 {
1030     if (paintingDisabled())
1031         return;
1032     GC2Canvas(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y));
1033 }
1034 
concatCTM(const AffineTransform & affine)1035 void GraphicsContext::concatCTM(const AffineTransform& affine)
1036 {
1037     if (paintingDisabled())
1038         return;
1039     GC2Canvas(this)->concat(affine);
1040 }
1041 
1042 /*  This is intended to round the rect to device pixels (through the CTM)
1043     and then invert the result back into source space, with the hope that when
1044     it is drawn (through the matrix), it will land in the "right" place (i.e.
1045     on pixel boundaries).
1046 
1047     For android, we record this geometry once and then draw it though various
1048     scale factors as the user zooms, without re-recording. Thus this routine
1049     should just leave the original geometry alone.
1050 
1051     If we instead draw into bitmap tiles, we should then perform this
1052     transform -> round -> inverse step.
1053  */
roundToDevicePixels(const FloatRect & rect)1054 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
1055 {
1056     return rect;
1057 }
1058 
1059 //////////////////////////////////////////////////////////////////////////////////////////////////
1060 
setURLForRect(const KURL & link,const IntRect & destRect)1061 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1062 {
1063 // appears to be PDF specific, so we ignore it
1064 #if 0
1065 if (paintingDisabled())
1066     return;
1067 
1068 CFURLRef urlRef = link.createCFURL();
1069 if (urlRef) {
1070     CGContextRef context = platformContext();
1071 
1072     // Get the bounding box to handle clipping.
1073     CGRect box = CGContextGetClipBoundingBox(context);
1074 
1075     IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
1076     IntRect rect = destRect;
1077     rect.intersect(intBox);
1078 
1079     CGPDFContextSetURLForRect(context, urlRef,
1080         CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
1081 
1082     CFRelease(urlRef);
1083 }
1084 #endif
1085 }
1086 
setPlatformShouldAntialias(bool useAA)1087 void GraphicsContext::setPlatformShouldAntialias(bool useAA)
1088 {
1089     if (paintingDisabled())
1090         return;
1091     m_data->mState->mUseAA = useAA;
1092 }
1093 
getCTM() const1094 AffineTransform GraphicsContext::getCTM() const
1095 {
1096     const SkMatrix& m = GC2Canvas(this)->getTotalMatrix();
1097     return AffineTransform(SkScalarToDouble(m.getScaleX()),      // a
1098                            SkScalarToDouble(m.getSkewY()),       // b
1099                            SkScalarToDouble(m.getSkewX()),       // c
1100                            SkScalarToDouble(m.getScaleY()),      // d
1101                            SkScalarToDouble(m.getTranslateX()),  // e
1102                            SkScalarToDouble(m.getTranslateY())); // f
1103 }
1104 
1105 ///////////////////////////////////////////////////////////////////////////////
1106 
beginPath()1107 void GraphicsContext::beginPath()
1108 {
1109     m_data->beginPath();
1110 }
1111 
addPath(const Path & p)1112 void GraphicsContext::addPath(const Path& p)
1113 {
1114     m_data->addPath(*p.platformPath());
1115 }
1116 
fillPath()1117 void GraphicsContext::fillPath()
1118 {
1119     SkPath* path = m_data->getPath();
1120     if (paintingDisabled() || !path)
1121         return;
1122 
1123     switch (this->fillRule()) {
1124         case RULE_NONZERO:
1125             path->setFillType(SkPath::kWinding_FillType);
1126             break;
1127         case RULE_EVENODD:
1128             path->setFillType(SkPath::kEvenOdd_FillType);
1129             break;
1130     }
1131 
1132     SkPaint paint;
1133     m_data->setup_paint_fill(&paint);
1134 
1135     extactShader(&paint,
1136                  m_common->state.fillPattern.get(),
1137                  m_common->state.fillGradient.get());
1138 
1139     GC2Canvas(this)->drawPath(*path, paint);
1140 }
1141 
strokePath()1142 void GraphicsContext::strokePath()
1143 {
1144     const SkPath* path = m_data->getPath();
1145     if (paintingDisabled() || !path)
1146         return;
1147 
1148     SkPaint paint;
1149     m_data->setup_paint_stroke(&paint, NULL);
1150 
1151     extactShader(&paint,
1152                  m_common->state.strokePattern.get(),
1153                  m_common->state.strokeGradient.get());
1154 
1155     GC2Canvas(this)->drawPath(*path, paint);
1156 }
1157 
setImageInterpolationQuality(InterpolationQuality mode)1158 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
1159 {
1160     /*
1161     enum InterpolationQuality {
1162         InterpolationDefault,
1163         InterpolationNone,
1164         InterpolationLow,
1165         InterpolationMedium,
1166         InterpolationHigh
1167     };
1168 
1169      TODO: record this, so we can know when to use bitmap-filtering when we draw
1170      ... not sure how meaningful this will be given our playback model.
1171 
1172      Certainly safe to do nothing for the present.
1173      */
1174 }
1175 
1176 } // namespace WebCore
1177 
1178 ///////////////////////////////////////////////////////////////////////////////
1179 
android_gc2canvas(WebCore::GraphicsContext * gc)1180 SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc)
1181 {
1182     return gc->platformContext()->mCanvas;
1183 }
1184