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