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