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