summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp')
-rw-r--r--Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp1011
1 files changed, 1004 insertions, 7 deletions
diff --git a/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp
index 098534c..3c8ea3c 100644
--- a/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp
+++ b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp
@@ -24,28 +24,1025 @@
*/
#include "config.h"
-#include "Node.h"
#include "PlatformGraphicsContext.h"
+
+#include "AffineTransform.h"
+#include "Font.h"
+#include "Gradient.h"
+#include "GraphicsContext.h"
+#include "Node.h"
+#include "NotImplemented.h"
+#include "Path.h"
+#include "Pattern.h"
+#include "SkBitmapRef.h"
+#include "SkBlurDrawLooper.h"
+#include "SkBlurMaskFilter.h"
#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkDevice.h"
+#include "SkGradientShader.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkiaUtils.h"
+#include "TransformationMatrix.h"
+#include "android_graphics.h"
namespace WebCore {
-PlatformGraphicsContext::PlatformGraphicsContext(SkCanvas* canvas)
- : mCanvas(canvas), m_deleteCanvas(false)
+// These are the flags we need when we call saveLayer for transparency.
+// Since it does not appear that webkit intends this to also save/restore
+// the matrix or clip, I do not give those flags (for performance)
+#define TRANSPARENCY_SAVEFLAGS \
+ (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | \
+ SkCanvas::kFullColorLayer_SaveFlag)
+
+//**************************************
+// Helper functions
+//**************************************
+
+static int RoundToInt(float x)
+{
+ return (int)roundf(x);
+}
+
+template <typename T> T* deepCopyPtr(const T* src)
+{
+ return src ? new T(*src) : 0;
+}
+
+// Set a bitmap shader that mimics dashing by width-on, width-off.
+// Returns false if it could not succeed (e.g. there was an existing shader)
+static bool setBitmapDash(SkPaint* paint, int width) {
+ if (width <= 0 || paint->getShader())
+ return false;
+
+ SkColor c = paint->getColor();
+
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1);
+ bm.allocPixels();
+ bm.lockPixels();
+
+ // set the ON pixel
+ *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c),
+ SkColorGetG(c), SkColorGetB(c));
+ // set the OFF pixel
+ *bm.getAddr32(1, 0) = 0;
+ bm.unlockPixels();
+
+ SkMatrix matrix;
+ matrix.setScale(SkIntToScalar(width), SK_Scalar1);
+
+ SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+ SkShader::kClamp_TileMode);
+ s->setLocalMatrix(matrix);
+
+ paint->setShader(s)->unref();
+ return true;
+}
+
+static void setrectForUnderline(SkRect* r, float lineThickness,
+ const FloatPoint& point, int yOffset, float width)
+{
+#if 0
+ if (lineThickness < 1) // Do we really need/want this?
+ lineThickness = 1;
+#endif
+ r->fLeft = point.x();
+ r->fTop = point.y() + yOffset;
+ r->fRight = r->fLeft + width;
+ r->fBottom = r->fTop + lineThickness;
+}
+
+static inline int fastMod(int value, int max)
+{
+ int sign = SkExtractSign(value);
+
+ value = SkApplySign(value, sign);
+ if (value >= max)
+ value %= max;
+ return SkApplySign(value, sign);
+}
+
+static inline void fixPaintForBitmapsThatMaySeam(SkPaint* paint) {
+ /* Bitmaps may be drawn to seem next to other images. If we are drawn
+ zoomed, or at fractional coordinates, we may see cracks/edges if
+ we antialias, because that will cause us to draw the same pixels
+ more than once (e.g. from the left and right bitmaps that share
+ an edge).
+
+ Disabling antialiasing fixes this, and since so far we are never
+ rotated at non-multiple-of-90 angles, this seems to do no harm
+ */
+ paint->setAntiAlias(false);
+}
+
+//**************************************
+// State structs
+//**************************************
+
+struct ShadowRec {
+ SkScalar blur;
+ SkScalar dx;
+ SkScalar dy;
+ SkColor color; // alpha>0 means valid shadow
+ ShadowRec(SkScalar b = 0,
+ SkScalar x = 0,
+ SkScalar y = 0,
+ SkColor c = 0) // by default, alpha=0, so no shadow
+ : blur(b), dx(x), dy(y), color(c)
+ {};
+};
+
+struct PlatformGraphicsContext::State {
+ SkPathEffect* pathEffect;
+ float miterLimit;
+ float alpha;
+ float strokeThickness;
+ SkPaint::Cap lineCap;
+ SkPaint::Join lineJoin;
+ SkXfermode::Mode mode;
+ int dashRatio; // Ratio of the length of a dash to its width
+ ShadowRec shadow;
+ SkColor fillColor;
+ SkShader* fillShader;
+ SkColor strokeColor;
+ SkShader* strokeShader;
+ bool useAA;
+ StrokeStyle strokeStyle;
+
+ State()
+ : pathEffect(0)
+ , miterLimit(4)
+ , alpha(1)
+ , strokeThickness(0) // Same as default in GraphicsContextPrivate.h
+ , lineCap(SkPaint::kDefault_Cap)
+ , lineJoin(SkPaint::kDefault_Join)
+ , mode(SkXfermode::kSrcOver_Mode)
+ , dashRatio(3)
+ , fillColor(SK_ColorBLACK)
+ , fillShader(0)
+ , strokeColor(SK_ColorBLACK)
+ , strokeShader(0)
+ , useAA(true)
+ , strokeStyle(SolidStroke)
+ {
+ }
+
+ State(const State& other)
+ : pathEffect(other.pathEffect)
+ , miterLimit(other.miterLimit)
+ , alpha(other.alpha)
+ , strokeThickness(other.strokeThickness)
+ , lineCap(other.lineCap)
+ , lineJoin(other.lineJoin)
+ , mode(other.mode)
+ , dashRatio(other.dashRatio)
+ , shadow(other.shadow)
+ , fillColor(other.fillColor)
+ , fillShader(other.fillShader)
+ , strokeColor(other.strokeColor)
+ , strokeShader(other.strokeShader)
+ , useAA(other.useAA)
+ , strokeStyle(other.strokeStyle)
+ {
+ SkSafeRef(pathEffect);
+ SkSafeRef(fillShader);
+ SkSafeRef(strokeShader);
+ }
+
+ ~State()
+ {
+ SkSafeUnref(pathEffect);
+ SkSafeUnref(fillShader);
+ SkSafeUnref(strokeShader);
+ }
+
+ void setShadow(int radius, int dx, int dy, SkColor c)
+ {
+ // Cut the radius in half, to visually match the effect seen in
+ // safari browser
+ shadow.blur = SkScalarHalf(SkIntToScalar(radius));
+ shadow.dx = SkIntToScalar(dx);
+ shadow.dy = SkIntToScalar(dy);
+ shadow.color = c;
+ }
+
+ bool setupShadowPaint(SkPaint* paint, SkPoint* offset,
+ bool shadowsIgnoreTransforms)
+ {
+ paint->setAntiAlias(true);
+ paint->setDither(true);
+ paint->setXfermodeMode(mode);
+ paint->setColor(shadow.color);
+ offset->set(shadow.dx, shadow.dy);
+
+ // Currently, only GraphicsContexts associated with the
+ // HTMLCanvasElement have shadows ignore transforms set. This
+ // allows us to distinguish between CSS and Canvas shadows which
+ // have different rendering specifications.
+ uint32_t flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
+ if (shadowsIgnoreTransforms) {
+ offset->fY = -offset->fY;
+ flags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag;
+ }
+
+ if (shadow.blur > 0) {
+ paint->setMaskFilter(SkBlurMaskFilter::Create(shadow.blur,
+ SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+ }
+ return SkColorGetA(shadow.color) && (shadow.blur || shadow.dx || shadow.dy);
+ }
+
+ SkColor applyAlpha(SkColor c) const
+ {
+ int s = RoundToInt(alpha * 256);
+ if (s >= 256)
+ return c;
+ if (s < 0)
+ return 0;
+
+ int a = SkAlphaMul(SkColorGetA(c), s);
+ return (c & 0x00FFFFFF) | (a << 24);
+ }
+
+ PlatformGraphicsContext::State cloneInheritedProperties();
+private:
+ // Not supported.
+ void operator=(const State&);
+};
+
+// Returns a new State with all of this object's inherited properties copied.
+PlatformGraphicsContext::State PlatformGraphicsContext::State::cloneInheritedProperties()
{
+ return PlatformGraphicsContext::State(*this);
}
-PlatformGraphicsContext::PlatformGraphicsContext()
- : mCanvas(new SkCanvas), m_deleteCanvas(true)
+//**************************************
+// PlatformGraphicsContext
+//**************************************
+
+PlatformGraphicsContext::PlatformGraphicsContext(SkCanvas* canvas,
+ bool takeCanvasOwnership)
+ : mCanvas(canvas)
+ , m_deleteCanvas(takeCanvasOwnership)
+ , m_stateStack(sizeof(State))
+ , m_gc(0)
{
+ m_stateStack.append(State());
+ m_state = &m_stateStack.last();
}
PlatformGraphicsContext::~PlatformGraphicsContext()
{
- if (m_deleteCanvas) {
-// printf("-------------------- deleting offscreen canvas\n");
+ if (m_deleteCanvas)
delete mCanvas;
+}
+
+//**************************************
+// State management
+//**************************************
+
+void PlatformGraphicsContext::beginTransparencyLayer(float opacity)
+{
+ SkCanvas* canvas = mCanvas;
+ canvas->saveLayerAlpha(0, (int)(opacity * 255), TRANSPARENCY_SAVEFLAGS);
+}
+
+void PlatformGraphicsContext::endTransparencyLayer()
+{
+ mCanvas->restore();
+}
+
+void PlatformGraphicsContext::save()
+{
+ m_stateStack.append(m_state->cloneInheritedProperties());
+ m_state = &m_stateStack.last();
+
+ // Save our native canvas.
+ mCanvas->save();
+}
+
+void PlatformGraphicsContext::restore()
+{
+ m_stateStack.removeLast();
+ m_state = &m_stateStack.last();
+
+ // Restore our native canvas.
+ mCanvas->restore();
+}
+
+//**************************************
+// State setters
+//**************************************
+
+void PlatformGraphicsContext::setAlpha(float alpha)
+{
+ m_state->alpha = alpha;
+}
+
+void PlatformGraphicsContext::setCompositeOperation(CompositeOperator op)
+{
+ m_state->mode = WebCoreCompositeToSkiaComposite(op);
+}
+
+void PlatformGraphicsContext::setFillColor(const Color& c)
+{
+ m_state->fillColor = c.rgb();
+ setFillShader(0);
+}
+
+void PlatformGraphicsContext::setFillShader(SkShader* fillShader)
+{
+ if (fillShader)
+ m_state->fillColor = Color::black;
+
+ if (fillShader != m_state->fillShader) {
+ SkSafeUnref(m_state->fillShader);
+ m_state->fillShader = fillShader;
+ SkSafeRef(m_state->fillShader);
+ }
+}
+
+void PlatformGraphicsContext::setLineCap(LineCap cap)
+{
+ switch (cap) {
+ case ButtCap:
+ m_state->lineCap = SkPaint::kButt_Cap;
+ break;
+ case RoundCap:
+ m_state->lineCap = SkPaint::kRound_Cap;
+ break;
+ case SquareCap:
+ m_state->lineCap = SkPaint::kSquare_Cap;
+ break;
+ default:
+ SkDEBUGF(("PlatformGraphicsContext::setLineCap: unknown LineCap %d\n", cap));
+ break;
+ }
+}
+
+void PlatformGraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
+{
+ size_t dashLength = dashes.size();
+ if (!dashLength)
+ return;
+
+ size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
+ SkScalar* intervals = new SkScalar[count];
+
+ for (unsigned int i = 0; i < count; i++)
+ intervals[i] = SkFloatToScalar(dashes[i % dashLength]);
+ SkPathEffect **effectPtr = &m_state->pathEffect;
+ SkSafeUnref(*effectPtr);
+ *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset));
+
+ delete[] intervals;
+}
+
+void PlatformGraphicsContext::setLineJoin(LineJoin join)
+{
+ switch (join) {
+ case MiterJoin:
+ m_state->lineJoin = SkPaint::kMiter_Join;
+ break;
+ case RoundJoin:
+ m_state->lineJoin = SkPaint::kRound_Join;
+ break;
+ case BevelJoin:
+ m_state->lineJoin = SkPaint::kBevel_Join;
+ break;
+ default:
+ SkDEBUGF(("PlatformGraphicsContext::setLineJoin: unknown LineJoin %d\n", join));
+ break;
+ }
+}
+
+void PlatformGraphicsContext::setMiterLimit(float limit)
+{
+ m_state->miterLimit = limit;
+}
+
+void PlatformGraphicsContext::setShadow(int radius, int dx, int dy, SkColor c)
+{
+ m_state->setShadow(radius, dx, dy, c);
+}
+
+void PlatformGraphicsContext::setShouldAntialias(bool useAA)
+{
+ m_state->useAA = useAA;
+}
+
+void PlatformGraphicsContext::setStrokeColor(const Color& c)
+{
+ m_state->strokeColor = c.rgb();
+ setStrokeShader(0);
+}
+
+void PlatformGraphicsContext::setStrokeShader(SkShader* strokeShader)
+{
+ if (strokeShader)
+ m_state->strokeColor = Color::black;
+
+ if (strokeShader != m_state->strokeShader) {
+ SkSafeUnref(m_state->strokeShader);
+ m_state->strokeShader = strokeShader;
+ SkSafeRef(m_state->strokeShader);
+ }
+}
+
+void PlatformGraphicsContext::setStrokeStyle(StrokeStyle style)
+{
+ m_state->strokeStyle = style;
+}
+
+void PlatformGraphicsContext::setStrokeThickness(float f)
+{
+ m_state->strokeThickness = f;
+}
+
+//**************************************
+// Paint setup
+//**************************************
+
+void PlatformGraphicsContext::setupPaintCommon(SkPaint* paint) const
+{
+ paint->setAntiAlias(m_state->useAA);
+ paint->setDither(true);
+ paint->setXfermodeMode(m_state->mode);
+ if (SkColorGetA(m_state->shadow.color) > 0) {
+
+ // Currently, only GraphicsContexts associated with the
+ // HTMLCanvasElement have shadows ignore transforms set. This
+ // allows us to distinguish between CSS and Canvas shadows which
+ // have different rendering specifications.
+ SkScalar dy = m_state->shadow.dy;
+ uint32_t flags = SkBlurDrawLooper::kHighQuality_BlurFlag;
+ if (shadowsIgnoreTransforms()) {
+ dy = -dy;
+ flags |= SkBlurDrawLooper::kIgnoreTransform_BlurFlag;
+ flags |= SkBlurDrawLooper::kOverrideColor_BlurFlag;
+ }
+
+ SkDrawLooper* looper = new SkBlurDrawLooper(m_state->shadow.blur,
+ m_state->shadow.dx,
+ dy,
+ m_state->shadow.color,
+ flags);
+ paint->setLooper(looper)->unref();
+ }
+ paint->setFilterBitmap(true);
+}
+
+void PlatformGraphicsContext::setupPaintFill(SkPaint* paint) const
+{
+ this->setupPaintCommon(paint);
+ paint->setColor(m_state->applyAlpha(m_state->fillColor));
+ paint->setShader(m_state->fillShader);
+}
+
+bool PlatformGraphicsContext::setupPaintShadow(SkPaint* paint, SkPoint* offset) const
+{
+ return m_state->setupShadowPaint(paint, offset, shadowsIgnoreTransforms());
+}
+
+bool PlatformGraphicsContext::setupPaintStroke(SkPaint* paint, SkRect* rect,
+ bool isHLine)
+{
+ this->setupPaintCommon(paint);
+ paint->setColor(m_state->applyAlpha(m_state->strokeColor));
+ paint->setShader(m_state->strokeShader);
+
+ float width = m_state->strokeThickness;
+
+ // This allows dashing and dotting to work properly for hairline strokes
+ // FIXME: Should we only do this for dashed and dotted strokes?
+ if (!width)
+ width = 1;
+
+ paint->setStyle(SkPaint::kStroke_Style);
+ paint->setStrokeWidth(SkFloatToScalar(width));
+ paint->setStrokeCap(m_state->lineCap);
+ paint->setStrokeJoin(m_state->lineJoin);
+ paint->setStrokeMiter(SkFloatToScalar(m_state->miterLimit));
+
+ if (rect && (RoundToInt(width) & 1))
+ rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
+
+ SkPathEffect* pe = m_state->pathEffect;
+ if (pe) {
+ paint->setPathEffect(pe);
+ return false;
+ }
+ switch (m_state->strokeStyle) {
+ case NoStroke:
+ case SolidStroke:
+ width = 0;
+ break;
+ case DashedStroke:
+ width = m_state->dashRatio * width;
+ break;
+ // No break
+ case DottedStroke:
+ break;
+ }
+
+ if (width > 0) {
+ // Return true if we're basically a dotted dash of squares
+ bool justSqrs = RoundToInt(width) == RoundToInt(paint->getStrokeWidth());
+
+ if (justSqrs || !isHLine || !setBitmapDash(paint, width)) {
+#if 0
+ // this is slow enough that we just skip it for now
+ // see http://b/issue?id=4163023
+ SkScalar intervals[] = { width, width };
+ pe = new SkDashPathEffect(intervals, 2, 0);
+ paint->setPathEffect(pe)->unref();
+#endif
+ }
+ return justSqrs;
+ }
+ return false;
+}
+
+//**************************************
+// Matrix operations
+//**************************************
+
+void PlatformGraphicsContext::concatCTM(const AffineTransform& affine)
+{
+ mCanvas->concat(affine);
+}
+
+void PlatformGraphicsContext::rotate(float angleInRadians)
+{
+ mCanvas->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f)));
+}
+
+void PlatformGraphicsContext::scale(const FloatSize& size)
+{
+ mCanvas->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
+}
+
+void PlatformGraphicsContext::translate(float x, float y)
+{
+ mCanvas->translate(SkFloatToScalar(x), SkFloatToScalar(y));
+}
+
+//**************************************
+// Clipping
+//**************************************
+
+void PlatformGraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
+ int thickness)
+{
+ SkPath path;
+ SkRect r(rect);
+
+ path.addOval(r, SkPath::kCW_Direction);
+ // Only perform the inset if we won't invert r
+ if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
+ // Adding one to the thickness doesn't make the border too thick as
+ // it's painted over afterwards. But without this adjustment the
+ // border appears a little anemic after anti-aliasing.
+ r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1));
+ path.addOval(r, SkPath::kCCW_Direction);
+ }
+ mCanvas->clipPath(path, SkRegion::kIntersect_Op, true);
+}
+
+void PlatformGraphicsContext::canvasClip(const Path& path)
+{
+ clip(path);
+}
+
+void PlatformGraphicsContext::clip(const FloatRect& rect)
+{
+ mCanvas->clipRect(rect);
+}
+
+void PlatformGraphicsContext::clip(const Path& path)
+{
+ mCanvas->clipPath(*path.platformPath(), SkRegion::kIntersect_Op, true);
+}
+
+void PlatformGraphicsContext::clipConvexPolygon(size_t numPoints,
+ const FloatPoint*, bool antialias)
+{
+ if (numPoints <= 1)
+ return;
+
+ // This is only used if HAVE_PATH_BASED_BORDER_RADIUS_DRAWING is defined
+ // in RenderObject.h which it isn't for us. TODO: Support that :)
+}
+
+void PlatformGraphicsContext::clipOut(const IntRect& r)
+{
+ mCanvas->clipRect(r, SkRegion::kDifference_Op);
+}
+
+void PlatformGraphicsContext::clipOut(const Path& p)
+{
+ mCanvas->clipPath(*p.platformPath(), SkRegion::kDifference_Op);
+}
+
+void PlatformGraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
+{
+ SkPath path = *pathToClip.platformPath();
+ path.setFillType(clipRule == RULE_EVENODD
+ ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
+ mCanvas->clipPath(path);
+}
+void PlatformGraphicsContext::clearRect(const FloatRect& rect)
+{
+ SkPaint paint;
+
+ setupPaintFill(&paint);
+ paint.setXfermodeMode(SkXfermode::kClear_Mode);
+ mCanvas->drawRect(rect, paint);
+}
+
+//**************************************
+// Drawing
+//**************************************
+
+void PlatformGraphicsContext::drawBitmapPattern(
+ const SkBitmap& bitmap, const SkMatrix& matrix,
+ CompositeOperator compositeOp, const FloatRect& destRect)
+{
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ shader->setLocalMatrix(matrix);
+ SkPaint paint;
+ setupPaintFill(&paint);
+ paint.setShader(shader);
+ paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
+ fixPaintForBitmapsThatMaySeam(&paint);
+ mCanvas->drawRect(destRect, paint);
+}
+
+void PlatformGraphicsContext::drawBitmapRect(const SkBitmap& bitmap,
+ const SkIRect* src, const SkRect& dst,
+ CompositeOperator op)
+{
+ SkPaint paint;
+ setupPaintFill(&paint);
+ paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(op));
+ fixPaintForBitmapsThatMaySeam(&paint);
+ mCanvas->drawBitmapRect(bitmap, src, dst, &paint);
+}
+
+void PlatformGraphicsContext::drawConvexPolygon(size_t numPoints,
+ const FloatPoint* points,
+ bool shouldAntialias)
+{
+ if (numPoints <= 1)
+ return;
+
+ SkPaint paint;
+ SkPath path;
+
+ path.incReserve(numPoints);
+ path.moveTo(SkFloatToScalar(points[0].x()), SkFloatToScalar(points[0].y()));
+ for (size_t i = 1; i < numPoints; i++)
+ path.lineTo(SkFloatToScalar(points[i].x()), SkFloatToScalar(points[i].y()));
+
+ if (mCanvas->quickReject(path, shouldAntialias ?
+ SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) {
+ return;
+ }
+
+ if (m_state->fillColor & 0xFF000000) {
+ setupPaintFill(&paint);
+ paint.setAntiAlias(shouldAntialias);
+ mCanvas->drawPath(path, paint);
+ }
+
+ if (m_state->strokeStyle != NoStroke) {
+ paint.reset();
+ setupPaintStroke(&paint, 0);
+ paint.setAntiAlias(shouldAntialias);
+ mCanvas->drawPath(path, paint);
+ }
+}
+
+void PlatformGraphicsContext::drawEllipse(const IntRect& rect)
+{
+ SkPaint paint;
+ SkRect oval(rect);
+
+ if (m_state->fillColor & 0xFF000000) {
+ setupPaintFill(&paint);
+ mCanvas->drawOval(oval, paint);
+ }
+ if (m_state->strokeStyle != NoStroke) {
+ paint.reset();
+ setupPaintStroke(&paint, &oval);
+ mCanvas->drawOval(oval, paint);
+ }
+}
+
+void PlatformGraphicsContext::drawFocusRing(const Vector<IntRect>& rects,
+ int /* width */, int /* offset */,
+ const Color& color)
+{
+ unsigned rectCount = rects.size();
+ if (!rectCount)
+ return;
+
+ SkRegion focusRingRegion;
+ const SkScalar focusRingOutset = WebCoreFloatToSkScalar(0.8);
+ for (unsigned i = 0; i < rectCount; i++) {
+ SkIRect r = rects[i];
+ r.inset(-focusRingOutset, -focusRingOutset);
+ focusRingRegion.op(r, SkRegion::kUnion_Op);
+ }
+
+ SkPath path;
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ paint.setColor(color.rgb());
+ paint.setStrokeWidth(focusRingOutset * 2);
+ paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref();
+ focusRingRegion.getBoundaryPath(&path);
+ mCanvas->drawPath(path, paint);
+}
+
+void PlatformGraphicsContext::drawHighlightForText(
+ const Font& font, const TextRun& run, const FloatPoint& point, int h,
+ const Color& backgroundColor, ColorSpace colorSpace, int from,
+ int to, bool isActive)
+{
+ IntRect rect = (IntRect)font.selectionRectForText(run, point, h, from, to);
+ if (isActive)
+ fillRect(rect, backgroundColor, colorSpace);
+ else {
+ int x = rect.x(), y = rect.y(), w = rect.width(), h = rect.height();
+ const int t = 3, t2 = t * 2;
+
+ fillRect(IntRect(x, y, w, t), backgroundColor, colorSpace);
+ fillRect(IntRect(x, y+h-t, w, t), backgroundColor, colorSpace);
+ fillRect(IntRect(x, y+t, t, h-t2), backgroundColor, colorSpace);
+ fillRect(IntRect(x+w-t, y+t, t, h-t2), backgroundColor, colorSpace);
+ }
+}
+
+void PlatformGraphicsContext::drawLine(const IntPoint& point1,
+ const IntPoint& point2)
+{
+ StrokeStyle style = m_state->strokeStyle;
+ if (style == NoStroke)
+ return;
+
+ SkPaint paint;
+ SkCanvas* canvas = mCanvas;
+ const int idx = SkAbs32(point2.x() - point1.x());
+ const int idy = SkAbs32(point2.y() - point1.y());
+
+ // Special-case horizontal and vertical lines that are really just dots
+ if (setupPaintStroke(&paint, 0, !idy) && (!idx || !idy)) {
+ const SkScalar diameter = paint.getStrokeWidth();
+ const SkScalar radius = SkScalarHalf(diameter);
+ SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x()));
+ SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y()));
+ SkScalar dx, dy;
+ int count;
+ SkRect bounds;
+
+ if (!idy) { // Horizontal
+ bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius);
+ x += radius;
+ dx = diameter * 2;
+ dy = 0;
+ count = idx;
+ } else { // Vertical
+ bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy));
+ y += radius;
+ dx = 0;
+ dy = diameter * 2;
+ count = idy;
+ }
+
+ // The actual count is the number of ONs we hit alternating
+ // ON(diameter), OFF(diameter), ...
+ {
+ SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter);
+ // Now compute the number of cells (ON and OFF)
+ count = SkScalarRound(width);
+ // Now compute the number of ONs
+ count = (count + 1) >> 1;
+ }
+
+ SkAutoMalloc storage(count * sizeof(SkPoint));
+ SkPoint* verts = (SkPoint*)storage.get();
+ // Now build the array of vertices to past to drawPoints
+ for (int i = 0; i < count; i++) {
+ verts[i].set(x, y);
+ x += dx;
+ y += dy;
+ }
+
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setPathEffect(0);
+
+ // Clipping to bounds is not required for correctness, but it does
+ // allow us to reject the entire array of points if we are completely
+ // offscreen. This is common in a webpage for android, where most of
+ // the content is clipped out. If drawPoints took an (optional) bounds
+ // parameter, that might even be better, as we would *just* use it for
+ // culling, and not both wacking the canvas' save/restore stack.
+ canvas->save(SkCanvas::kClip_SaveFlag);
+ canvas->clipRect(bounds);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint);
+ canvas->restore();
+ } else {
+ SkPoint pts[2] = { point1, point2 };
+ canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
+ }
+}
+
+void PlatformGraphicsContext::drawLineForText(const FloatPoint& pt, float width)
+{
+ SkRect r;
+ setrectForUnderline(&r, m_state->strokeThickness, pt, 0, width);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(m_state->strokeColor);
+
+ mCanvas->drawRect(r, paint);
+}
+
+void PlatformGraphicsContext::drawLineForTextChecking(const FloatPoint& pt,
+ float width, GraphicsContext::TextCheckingLineStyle)
+{
+ // TODO: Should we draw different based on TextCheckingLineStyle?
+ SkRect r;
+ setrectForUnderline(&r, m_state->strokeThickness, pt, 0, width);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorRED); // Is this specified somewhere?
+
+ mCanvas->drawRect(r, paint);
+}
+
+void PlatformGraphicsContext::drawRect(const IntRect& rect)
+{
+ SkPaint paint;
+ SkRect r(rect);
+
+ if (m_state->fillColor & 0xFF000000) {
+ setupPaintFill(&paint);
+ mCanvas->drawRect(r, paint);
+ }
+
+ // According to GraphicsContext.h, stroking inside drawRect always means
+ // a stroke of 1 inside the rect.
+ if (m_state->strokeStyle != NoStroke && (m_state->strokeColor & 0xFF000000)) {
+ paint.reset();
+ setupPaintStroke(&paint, &r);
+ paint.setPathEffect(0); // No dashing please
+ paint.setStrokeWidth(SK_Scalar1); // Always just 1.0 width
+ r.inset(SK_ScalarHalf, SK_ScalarHalf); // Ensure we're "inside"
+ mCanvas->drawRect(r, paint);
+ }
+}
+
+void PlatformGraphicsContext::fillPath(const Path& pathToFill, WindRule fillRule)
+{
+ SkPath* path = pathToFill.platformPath();
+ if (!path)
+ return;
+
+ switch (fillRule) {
+ case RULE_NONZERO:
+ path->setFillType(SkPath::kWinding_FillType);
+ break;
+ case RULE_EVENODD:
+ path->setFillType(SkPath::kEvenOdd_FillType);
+ break;
+ }
+
+ SkPaint paint;
+ setupPaintFill(&paint);
+
+ mCanvas->drawPath(*path, paint);
+}
+
+void PlatformGraphicsContext::fillRect(const FloatRect& rect)
+{
+ SkPaint paint;
+ setupPaintFill(&paint);
+ mCanvas->drawRect(rect, paint);
+}
+
+void PlatformGraphicsContext::fillRect(const FloatRect& rect,
+ const Color& color, ColorSpace)
+{
+ if (color.rgb() & 0xFF000000) {
+ SkPaint paint;
+
+ setupPaintCommon(&paint);
+ paint.setColor(color.rgb()); // Punch in the specified color
+ paint.setShader(0); // In case we had one set
+
+ // Sometimes we record and draw portions of the page, using clips
+ // for each portion. The problem with this is that webkit, sometimes,
+ // sees that we're only recording a portion, and they adjust some of
+ // their rectangle coordinates accordingly (e.g.
+ // RenderBoxModelObject::paintFillLayerExtended() which calls
+ // rect.intersect(paintInfo.rect) and then draws the bg with that
+ // rect. The result is that we end up drawing rects that are meant to
+ // seam together (one for each portion), but if the rects have
+ // fractional coordinates (e.g. we are zoomed by a fractional amount)
+ // we will double-draw those edges, resulting in visual cracks or
+ // artifacts.
+
+ // The fix seems to be to just turn off antialasing for rects (this
+ // entry-point in GraphicsContext seems to have been sufficient,
+ // though perhaps we'll find we need to do this as well in fillRect(r)
+ // as well.) Currently setupPaintCommon() enables antialiasing.
+
+ // Since we never show the page rotated at a funny angle, disabling
+ // antialiasing seems to have no real down-side, and it does fix the
+ // bug when we're zoomed (and drawing portions that need to seam).
+ paint.setAntiAlias(false);
+
+ mCanvas->drawRect(rect, paint);
}
}
+void PlatformGraphicsContext::fillRoundedRect(
+ const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
+ const IntSize& bottomLeft, const IntSize& bottomRight,
+ const Color& color, ColorSpace)
+{
+ SkPaint paint;
+ SkPath path;
+ SkScalar radii[8];
+
+ radii[0] = SkIntToScalar(topLeft.width());
+ radii[1] = SkIntToScalar(topLeft.height());
+ radii[2] = SkIntToScalar(topRight.width());
+ radii[3] = SkIntToScalar(topRight.height());
+ radii[4] = SkIntToScalar(bottomRight.width());
+ radii[5] = SkIntToScalar(bottomRight.height());
+ radii[6] = SkIntToScalar(bottomLeft.width());
+ radii[7] = SkIntToScalar(bottomLeft.height());
+ path.addRoundRect(rect, radii);
+
+ setupPaintFill(&paint);
+ paint.setColor(color.rgb());
+ mCanvas->drawPath(path, paint);
+}
+
+void PlatformGraphicsContext::strokeArc(const IntRect& r, int startAngle,
+ int angleSpan)
+{
+ SkPath path;
+ SkPaint paint;
+ SkRect oval(r);
+
+ if (m_state->strokeStyle == NoStroke) {
+ setupPaintFill(&paint); // We want the fill color
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkFloatToScalar(m_state->strokeThickness));
+ } else
+ setupPaintStroke(&paint, 0);
+
+ // We do this before converting to scalar, so we don't overflow SkFixed
+ startAngle = fastMod(startAngle, 360);
+ angleSpan = fastMod(angleSpan, 360);
+
+ path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
+ mCanvas->drawPath(path, paint);
+}
+
+void PlatformGraphicsContext::strokePath(const Path& pathToStroke)
+{
+ const SkPath* path = pathToStroke.platformPath();
+ if (!path)
+ return;
+
+ SkPaint paint;
+ setupPaintStroke(&paint, 0);
+
+ mCanvas->drawPath(*path, paint);
+}
+
+void PlatformGraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
+{
+ SkPaint paint;
+
+ setupPaintStroke(&paint, 0);
+ paint.setStrokeWidth(SkFloatToScalar(lineWidth));
+ mCanvas->drawRect(rect, paint);
+}
+
} // WebCore