summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
commit8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch)
tree11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebCore/platform/graphics/android/GraphicsContextAndroid.cpp
parent648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff)
downloadexternal_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebCore/platform/graphics/android/GraphicsContextAndroid.cpp')
-rw-r--r--WebCore/platform/graphics/android/GraphicsContextAndroid.cpp1118
1 files changed, 1118 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp
new file mode 100644
index 0000000..c2e0f02
--- /dev/null
+++ b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp
@@ -0,0 +1,1118 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Gradient.h"
+#include "GraphicsContext.h"
+#include "GraphicsContextPrivate.h"
+#include "NotImplemented.h"
+#include "Path.h"
+#include "Pattern.h"
+
+#include "SkBlurDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkDashPathEffect.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkPorterDuff.h"
+#include "PlatformGraphicsContext.h"
+#include "AffineTransform.h"
+
+#include "android_graphics.h"
+#include "SkGradientShader.h"
+#include "SkBitmapRef.h"
+#include "SkString.h"
+
+using namespace std;
+
+#define GC2Canvas(ctx) (ctx)->m_data->mPgc->mCanvas
+
+namespace WebCore {
+
+static int RoundToInt(float x)
+{
+ return (int)roundf(x);
+}
+
+template <typename T> T* deepCopyPtr(const T* src) {
+ return src ? new T(*src) : NULL;
+}
+
+/* TODO / questions
+
+ mAlpha: how does this interact with the alpha in Color? multiply them together?
+ mPorterDuffMode: do I always respect this? If so, then
+ the rgb() & 0xFF000000 check will abort drawing too often
+ Is Color premultiplied or not? If it is, then I can't blindly pass it to paint.setColor()
+*/
+
+struct ShadowRec {
+ SkScalar mBlur; // >0 means valid shadow
+ SkScalar mDx;
+ SkScalar mDy;
+ SkColor mColor;
+};
+
+class GraphicsContextPlatformPrivate {
+public:
+ GraphicsContext* mCG; // back-ptr to our parent
+ PlatformGraphicsContext* mPgc;
+
+ struct State {
+ SkPath* mPath;
+ float mMiterLimit;
+ float mAlpha;
+ float mStrokeThickness;
+ SkPaint::Cap mLineCap;
+ SkPaint::Join mLineJoin;
+ SkPorterDuff::Mode mPorterDuffMode;
+ int mDashRatio; //ratio of the length of a dash to its width
+ ShadowRec mShadow;
+ SkColor mFillColor;
+ SkColor mStrokeColor;
+ bool mUseAA;
+
+ State() {
+ mPath = NULL; // lazily allocated
+ mMiterLimit = 4;
+ mAlpha = 1;
+ mStrokeThickness = 0.0f; // Same as default in GraphicsContextPrivate.h
+ mLineCap = SkPaint::kDefault_Cap;
+ mLineJoin = SkPaint::kDefault_Join;
+ mPorterDuffMode = SkPorterDuff::kSrcOver_Mode;
+ mDashRatio = 3;
+ mUseAA = true;
+ mShadow.mBlur = 0;
+ mFillColor = SK_ColorBLACK;
+ mStrokeColor = SK_ColorBLACK;
+ }
+
+ State(const State& other) {
+ memcpy(this, &other, sizeof(State));
+ mPath = deepCopyPtr<SkPath>(other.mPath);
+ }
+
+ ~State() {
+ delete mPath;
+ }
+
+ void setShadow(int radius, int dx, int dy, SkColor c) {
+ // cut the radius in half, to visually match the effect seen in
+ // safari browser
+ mShadow.mBlur = SkScalarHalf(SkIntToScalar(radius));
+ mShadow.mDx = SkIntToScalar(dx);
+ mShadow.mDy = SkIntToScalar(dy);
+ mShadow.mColor = c;
+ }
+
+ bool setupShadowPaint(SkPaint* paint, SkPoint* offset) {
+ if (mShadow.mBlur > 0) {
+ paint->setAntiAlias(true);
+ paint->setDither(true);
+ paint->setPorterDuffXfermode(mPorterDuffMode);
+ paint->setColor(mShadow.mColor);
+ paint->setMaskFilter(SkBlurMaskFilter::Create(mShadow.mBlur,
+ SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+ offset->set(mShadow.mDx, mShadow.mDy);
+ return true;
+ }
+ return false;
+ }
+
+ SkColor applyAlpha(SkColor c) const
+ {
+ int s = RoundToInt(mAlpha * 256);
+ if (s >= 256)
+ return c;
+ if (s < 0)
+ return 0;
+
+ int a = SkAlphaMul(SkColorGetA(c), s);
+ return (c & 0x00FFFFFF) | (a << 24);
+ }
+ };
+
+ SkDeque mStateStack;
+ State* mState;
+
+ GraphicsContextPlatformPrivate(GraphicsContext* cg, PlatformGraphicsContext* pgc)
+ : mCG(cg)
+ , mPgc(pgc), mStateStack(sizeof(State)) {
+ State* state = (State*)mStateStack.push_back();
+ new (state) State();
+ mState = state;
+ }
+
+ ~GraphicsContextPlatformPrivate() {
+ if (mPgc && mPgc->deleteUs())
+ delete mPgc;
+
+ // we force restores so we don't leak any subobjects owned by our
+ // stack of State records.
+ while (mStateStack.count() > 0)
+ this->restore();
+ }
+
+ void save() {
+ State* newState = (State*)mStateStack.push_back();
+ new (newState) State(*mState);
+ mState = newState;
+ }
+
+ void restore() {
+ mState->~State();
+ mStateStack.pop_back();
+ mState = (State*)mStateStack.back();
+ }
+
+ void setFillColor(const Color& c) {
+ mState->mFillColor = c.rgb();
+ }
+
+ void setStrokeColor(const Color& c) {
+ mState->mStrokeColor = c.rgb();
+ }
+
+ void setStrokeThickness(float f) {
+ mState->mStrokeThickness = f;
+ }
+
+ void beginPath() {
+ if (mState->mPath) {
+ mState->mPath->reset();
+ }
+ }
+
+ void addPath(const SkPath& other) {
+ if (!mState->mPath) {
+ mState->mPath = new SkPath(other);
+ } else {
+ mState->mPath->addPath(other);
+ }
+ }
+
+ // may return null
+ SkPath* getPath() const { return mState->mPath; }
+
+ void setup_paint_common(SkPaint* paint) const {
+ paint->setAntiAlias(mState->mUseAA);
+ paint->setDither(true);
+ paint->setPorterDuffXfermode(mState->mPorterDuffMode);
+ if (mState->mShadow.mBlur > 0) {
+ SkDrawLooper* looper = new SkBlurDrawLooper(mState->mShadow.mBlur,
+ mState->mShadow.mDx,
+ mState->mShadow.mDy,
+ mState->mShadow.mColor);
+ paint->setLooper(looper)->unref();
+ }
+
+ /* need to sniff textDrawingMode(), which returns the bit_OR of...
+ const int cTextInvisible = 0;
+ const int cTextFill = 1;
+ const int cTextStroke = 2;
+ const int cTextClip = 4;
+ */
+ }
+
+ void setup_paint_fill(SkPaint* paint) const {
+ this->setup_paint_common(paint);
+ paint->setColor(mState->mFillColor);
+ }
+
+ /* sets up the paint for stroking. Returns true if the style is really
+ just a dash of squares (the size of the paint's stroke-width.
+ */
+ bool setup_paint_stroke(SkPaint* paint, SkRect* rect) {
+ this->setup_paint_common(paint);
+ paint->setColor(mState->mStrokeColor);
+
+ float width = mState->mStrokeThickness;
+
+ // this allows dashing and dotting to work properly for hairline strokes
+ // FIXME: Should we only do this for dashed and dotted strokes?
+ if (0 == width) {
+ width = 1;
+ }
+
+// paint->setColor(mState->applyAlpha(mCG->strokeColor().rgb()));
+ paint->setStyle(SkPaint::kStroke_Style);
+ paint->setStrokeWidth(SkFloatToScalar(width));
+ paint->setStrokeCap(mState->mLineCap);
+ paint->setStrokeJoin(mState->mLineJoin);
+ paint->setStrokeMiter(SkFloatToScalar(mState->mMiterLimit));
+
+ if (rect != NULL && (RoundToInt(width) & 1)) {
+ rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
+ }
+
+ switch (mCG->strokeStyle()) {
+ case NoStroke:
+ case SolidStroke:
+ width = 0;
+ break;
+ case DashedStroke:
+ width = mState->mDashRatio * width;
+ break;
+ /* no break */
+ case DottedStroke:
+ break;
+ }
+
+ if (width > 0) {
+ SkScalar intervals[] = { width, width };
+ SkPathEffect* pe = new SkDashPathEffect(intervals, 2, 0);
+ paint->setPathEffect(pe)->unref();
+ // return true if we're basically a dotted dash of squares
+ return RoundToInt(width) == RoundToInt(paint->getStrokeWidth());
+ }
+ return false;
+ }
+
+private:
+ // not supported yet
+ State& operator=(const State&);
+};
+
+static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm) {
+ SkShader::TileMode mode = SkShader::kClamp_TileMode;
+
+ switch (sm) {
+ case SpreadMethodPad:
+ mode = SkShader::kClamp_TileMode;
+ break;
+ case SpreadMethodReflect:
+ mode = SkShader::kMirror_TileMode;
+ break;
+ case SpreadMethodRepeat:
+ mode = SkShader::kRepeat_TileMode;
+ break;
+ }
+ return mode;
+}
+
+static void extactShader(SkPaint* paint, ColorSpace cs, Pattern* pat,
+ Gradient* grad, GradientSpreadMethod sm) {
+ switch (cs) {
+ case PatternColorSpace:
+ // createPlatformPattern() returns a new inst
+ paint->setShader(pat->createPlatformPattern(
+ AffineTransform()))->safeUnref();
+ break;
+ case GradientColorSpace: {
+ // grad->getShader() returns a cached obj
+ paint->setShader(grad->getShader(SpreadMethod2TileMode(sm)));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height)
+{
+ PlatformGraphicsContext* pgc = new PlatformGraphicsContext();
+
+ SkBitmap bitmap;
+
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap.allocPixels();
+ bitmap.eraseColor(0);
+ pgc->mCanvas->setBitmapDevice(bitmap);
+
+ GraphicsContext* ctx = new GraphicsContext(pgc);
+//printf("-------- create offscreen <canvas> %p\n", ctx);
+ return ctx;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+GraphicsContext::GraphicsContext(PlatformGraphicsContext *gc)
+ : m_common(createGraphicsContextPrivate())
+ , m_data(new GraphicsContextPlatformPrivate(this, gc))
+{
+ setPaintingDisabled(NULL == gc || NULL == gc->mCanvas);
+}
+
+GraphicsContext::~GraphicsContext()
+{
+ delete m_data;
+ this->destroyGraphicsContextPrivate(m_common);
+}
+
+void GraphicsContext::savePlatformState()
+{
+ // save our private State
+ m_data->save();
+ // save our native canvas
+ GC2Canvas(this)->save();
+}
+
+void GraphicsContext::restorePlatformState()
+{
+ // restore our native canvas
+ GC2Canvas(this)->restore();
+ // restore our private State
+ m_data->restore();
+}
+
+bool GraphicsContext::willFill() const {
+ return m_data->mState->mFillColor != 0;
+}
+
+bool GraphicsContext::willStroke() const {
+ return m_data->mState->mStrokeColor != 0;
+}
+
+// Draws a filled rectangle with a stroked border.
+void GraphicsContext::drawRect(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+ SkRect r;
+
+ android_setrect(&r, rect);
+
+ if (fillColor().alpha()) {
+ m_data->setup_paint_fill(&paint);
+ GC2Canvas(this)->drawRect(r, paint);
+ }
+
+ if (strokeStyle() != NoStroke && strokeColor().alpha()) {
+ paint.reset();
+ m_data->setup_paint_stroke(&paint, &r);
+ GC2Canvas(this)->drawRect(r, paint);
+ }
+}
+
+// This is only used to draw borders.
+void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
+{
+ if (paintingDisabled())
+ return;
+
+ StrokeStyle style = strokeStyle();
+ if (style == NoStroke)
+ return;
+
+ SkPaint paint;
+ SkCanvas* canvas = GC2Canvas(this);
+ 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 (m_data->setup_paint_stroke(&paint, NULL) && (0 == idx || 0 == 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 (0 == 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 computer 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(NULL);
+
+ // 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];
+ android_setpt(&pts[0], point1);
+ android_setpt(&pts[1], point2);
+ canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
+ }
+}
+
+static void setrect_for_underline(SkRect* r, GraphicsContext* context, const IntPoint& point, int yOffset, int width)
+{
+ float lineThickness = context->strokeThickness();
+// if (lineThickness < 1) // do we really need/want this?
+// lineThickness = 1;
+
+ yOffset += 1; // we add 1 to have underlines appear below the text
+
+ r->fLeft = SkIntToScalar(point.x());
+ r->fTop = SkIntToScalar(point.y() + yOffset);
+ r->fRight = r->fLeft + SkIntToScalar(width);
+ r->fBottom = r->fTop + SkFloatToScalar(lineThickness);
+}
+
+void GraphicsContext::drawLineForText(IntPoint const& pt, int width, bool)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect r;
+ setrect_for_underline(&r, this, pt, 0, width);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(this->strokeColor().rgb());
+
+ GC2Canvas(this)->drawRect(r, paint);
+}
+
+void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& pt, int width, bool grammar)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect r;
+ setrect_for_underline(&r, this, pt, 0, width);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorRED); // is this specified somewhere?
+
+ GC2Canvas(this)->drawRect(r, paint);
+}
+
+// This method is only used to draw the little circles used in lists.
+void GraphicsContext::drawEllipse(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+ SkRect oval;
+
+ android_setrect(&oval, rect);
+
+ if (fillColor().rgb() & 0xFF000000) {
+ m_data->setup_paint_fill(&paint);
+ GC2Canvas(this)->drawOval(oval, paint);
+ }
+ if (strokeStyle() != NoStroke) {
+ paint.reset();
+ m_data->setup_paint_stroke(&paint, &oval);
+ GC2Canvas(this)->drawOval(oval, paint);
+ }
+}
+
+static inline int fast_mod(int value, int max)
+{
+ int sign = SkExtractSign(value);
+
+ value = SkApplySign(value, sign);
+ if (value >= max) {
+ value %= max;
+ }
+ return SkApplySign(value, sign);
+}
+
+void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPath path;
+ SkPaint paint;
+ SkRect oval;
+
+ android_setrect(&oval, r);
+
+ if (strokeStyle() == NoStroke) {
+ m_data->setup_paint_fill(&paint); // we want the fill color
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkFloatToScalar(this->strokeThickness()));
+ }
+ else {
+ m_data->setup_paint_stroke(&paint, NULL);
+ }
+
+ // we do this before converting to scalar, so we don't overflow SkFixed
+ startAngle = fast_mod(startAngle, 360);
+ angleSpan = fast_mod(angleSpan, 360);
+
+ path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
+ GC2Canvas(this)->drawPath(path, paint);
+}
+
+void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
+{
+ if (paintingDisabled())
+ return;
+
+ 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 (GC2Canvas(this)->quickReject(path, shouldAntialias ?
+ SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) {
+ return;
+ }
+
+ if (fillColor().rgb() & 0xFF000000) {
+ m_data->setup_paint_fill(&paint);
+ paint.setAntiAlias(shouldAntialias);
+ GC2Canvas(this)->drawPath(path, paint);
+ }
+
+ if (strokeStyle() != NoStroke) {
+ paint.reset();
+ m_data->setup_paint_stroke(&paint, NULL);
+ paint.setAntiAlias(shouldAntialias);
+ GC2Canvas(this)->drawPath(path, paint);
+ }
+}
+
+void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
+ const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+ SkPath path;
+ SkScalar radii[8];
+ SkRect r;
+
+ 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(*android_setrect(&r, rect), radii);
+
+ m_data->setup_paint_fill(&paint);
+ GC2Canvas(this)->drawPath(path, paint);
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect)
+{
+ SkPaint paint;
+ SkRect r;
+
+ android_setrect(&r, rect);
+ m_data->setup_paint_fill(&paint);
+
+ extactShader(&paint, m_common->state.fillColorSpace,
+ m_common->state.fillPattern.get(),
+ m_common->state.fillGradient.get(), spreadMethod());
+
+ GC2Canvas(this)->drawRect(r, paint);
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ if (color.rgb() & 0xFF000000) {
+ SkPaint paint;
+ SkRect r;
+
+ android_setrect(&r, rect);
+ m_data->setup_paint_common(&paint);
+ paint.setColor(color.rgb()); // punch in the specified color
+ paint.setShader(NULL); // in case we had one set
+ GC2Canvas(this)->drawRect(r, paint);
+ }
+}
+
+void GraphicsContext::clip(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect r;
+
+ GC2Canvas(this)->clipRect(*android_setrect(&r, rect));
+}
+
+void GraphicsContext::clip(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+// path.platformPath()->dump(false, "clip path");
+
+ GC2Canvas(this)->clipPath(*path.platformPath());
+}
+
+void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
+{
+ if (paintingDisabled())
+ return;
+
+//printf("-------- addInnerRoundedRectClip: [%d %d %d %d] thickness=%d\n", rect.x(), rect.y(), rect.width(), rect.height(), thickness);
+
+ SkPath path;
+ SkRect r;
+ android_setrect(&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())
+ {
+ r.inset(SkIntToScalar(thickness) ,SkIntToScalar(thickness));
+ path.addOval(r, SkPath::kCCW_Direction);
+ }
+ GC2Canvas(this)->clipPath(path);
+}
+
+void GraphicsContext::clipOut(const IntRect& r)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect rect;
+
+ GC2Canvas(this)->clipRect(*android_setrect(&rect, r), SkRegion::kDifference_Op);
+}
+
+void GraphicsContext::clipOutEllipseInRect(const IntRect& r)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect oval;
+ SkPath path;
+
+ path.addOval(*android_setrect(&oval, r), SkPath::kCCW_Direction);
+ GC2Canvas(this)->clipPath(path, SkRegion::kDifference_Op);
+}
+
+void GraphicsContext::clipOut(const Path& p)
+{
+ if (paintingDisabled())
+ return;
+
+ GC2Canvas(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op);
+}
+
+void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) {
+ SkDebugf("xxxxxxxxxxxxxxxxxx clipToImageBuffer not implemented\n");
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if SVG_SUPPORT
+KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
+{
+ return new KRenderingDeviceContextQuartz(platformContext());
+}
+#endif
+
+void GraphicsContext::beginTransparencyLayer(float opacity)
+{
+ if (paintingDisabled())
+ return;
+
+ SkCanvas* canvas = GC2Canvas(this);
+
+ if (opacity < 1)
+ {
+ canvas->saveLayerAlpha(NULL, (int)(opacity * 255), SkCanvas::kHasAlphaLayer_SaveFlag);
+ }
+ else
+ canvas->save();
+}
+
+void GraphicsContext::endTransparencyLayer()
+{
+ if (paintingDisabled())
+ return;
+
+ GC2Canvas(this)->restore();
+}
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ void GraphicsContext::setupFillPaint(SkPaint* paint) {
+ m_data->setup_paint_fill(paint);
+ }
+
+ void GraphicsContext::setupStrokePaint(SkPaint* paint) {
+ m_data->setup_paint_stroke(paint, NULL);
+ }
+
+ bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset) {
+ return m_data->mState->setupShadowPaint(paint, offset);
+ }
+
+ // referenced from CanvasStyle.cpp
+ void GraphicsContext::setCMYKAFillColor(float c, float m, float y, float k, float a) {
+ float r = 1 - (c + k);
+ float g = 1 - (m + k);
+ float b = 1 - (y + k);
+ return this->setFillColor(Color(r, g, b, a));
+ }
+
+ // referenced from CanvasStyle.cpp
+ void GraphicsContext::setCMYKAStrokeColor(float c, float m, float y, float k, float a) {
+ float r = 1 - (c + k);
+ float g = 1 - (m + k);
+ float b = 1 - (y + k);
+ return this->setStrokeColor(Color(r, g, b, a));
+ }
+
+ void GraphicsContext::setPlatformStrokeColor(const Color& c) {
+ m_data->setStrokeColor(c);
+ }
+
+ void GraphicsContext::setPlatformStrokeThickness(float f) {
+ m_data->setStrokeThickness(f);
+ }
+
+ void GraphicsContext::setPlatformFillColor(const Color& c) {
+ m_data->setFillColor(c);
+ }
+
+void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ if (blur <= 0) {
+ this->clearPlatformShadow();
+ }
+
+ SkColor c;
+ if (color.isValid()) {
+ c = color.rgb();
+ } else {
+ c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" Apple shadow color
+ }
+ m_data->mState->setShadow(blur, size.width(), size.height(), c);
+}
+
+void GraphicsContext::clearPlatformShadow()
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->mState->setShadow(0, 0, 0, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GraphicsContext::drawFocusRing(const Color& color)
+{
+ // Do nothing, since we draw the focus ring independently.
+}
+
+PlatformGraphicsContext* GraphicsContext::platformContext() const
+{
+ ASSERT(!paintingDisabled());
+ return m_data->mPgc;
+}
+
+void GraphicsContext::setMiterLimit(float limit)
+{
+ m_data->mState->mMiterLimit = limit;
+}
+
+void GraphicsContext::setAlpha(float alpha)
+{
+ m_data->mState->mAlpha = alpha;
+}
+
+void GraphicsContext::setCompositeOperation(CompositeOperator op)
+{
+ m_data->mState->mPorterDuffMode = android_convert_compositeOp(op);
+}
+
+void GraphicsContext::clearRect(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+ SkRect r;
+
+ android_setrect(&r, rect);
+ m_data->setup_paint_fill(&paint);
+ paint.setPorterDuffXfermode(SkPorterDuff::kClear_Mode);
+ GC2Canvas(this)->drawRect(r, paint);
+}
+
+void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+ SkRect r;
+
+ android_setrect(&r, rect);
+
+ m_data->setup_paint_stroke(&paint, NULL);
+ paint.setStrokeWidth(SkFloatToScalar(lineWidth));
+ GC2Canvas(this)->drawRect(r, paint);
+}
+
+void GraphicsContext::setLineCap(LineCap cap)
+{
+ switch (cap) {
+ case ButtCap:
+ m_data->mState->mLineCap = SkPaint::kButt_Cap;
+ break;
+ case RoundCap:
+ m_data->mState->mLineCap = SkPaint::kRound_Cap;
+ break;
+ case SquareCap:
+ m_data->mState->mLineCap = SkPaint::kSquare_Cap;
+ break;
+ default:
+ SkDEBUGF(("GraphicsContext::setLineCap: unknown LineCap %d\n", cap));
+ break;
+ }
+}
+
+void GraphicsContext::setLineJoin(LineJoin join)
+{
+ switch (join) {
+ case MiterJoin:
+ m_data->mState->mLineJoin = SkPaint::kMiter_Join;
+ break;
+ case RoundJoin:
+ m_data->mState->mLineJoin = SkPaint::kRound_Join;
+ break;
+ case BevelJoin:
+ m_data->mState->mLineJoin = SkPaint::kBevel_Join;
+ break;
+ default:
+ SkDEBUGF(("GraphicsContext::setLineJoin: unknown LineJoin %d\n", join));
+ break;
+ }
+}
+
+void GraphicsContext::scale(const FloatSize& size)
+{
+ if (paintingDisabled())
+ return;
+ GC2Canvas(this)->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
+}
+
+void GraphicsContext::rotate(float angleInRadians)
+{
+ if (paintingDisabled())
+ return;
+ GC2Canvas(this)->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f)));
+}
+
+void GraphicsContext::translate(float x, float y)
+{
+ if (paintingDisabled())
+ return;
+ GC2Canvas(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y));
+}
+
+void GraphicsContext::concatCTM(const AffineTransform& xform)
+{
+ if (paintingDisabled())
+ return;
+
+//printf("-------------- GraphicsContext::concatCTM\n");
+ GC2Canvas(this)->concat((SkMatrix) xform);
+}
+
+FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return FloatRect();
+
+ const SkMatrix& matrix = GC2Canvas(this)->getTotalMatrix();
+ SkRect r;
+ android_setrect(&r, rect);
+ matrix.mapRect(&r);
+ FloatRect result(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.width()), SkScalarToFloat(r.height()));
+ return result;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
+{
+// appears to be PDF specific, so we ignore it
+#if 0
+if (paintingDisabled())
+ return;
+
+CFURLRef urlRef = link.createCFURL();
+if (urlRef) {
+ CGContextRef context = platformContext();
+
+ // Get the bounding box to handle clipping.
+ CGRect box = CGContextGetClipBoundingBox(context);
+
+ IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
+ IntRect rect = destRect;
+ rect.intersect(intBox);
+
+ CGPDFContextSetURLForRect(context, urlRef,
+ CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
+
+ CFRelease(urlRef);
+}
+#endif
+}
+
+void GraphicsContext::setUseAntialiasing(bool useAA) {
+ if (paintingDisabled())
+ return;
+ m_data->mState->mUseAA = useAA;
+}
+
+AffineTransform GraphicsContext::getCTM() const {
+ return AffineTransform(GC2Canvas(this)->getTotalMatrix());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GraphicsContext::beginPath() {
+ m_data->beginPath();
+}
+
+void GraphicsContext::addPath(const Path& p) {
+ m_data->addPath(*p.platformPath());
+}
+
+void GraphicsContext::drawPath() {
+ this->fillPath();
+ this->strokePath();
+}
+
+void GraphicsContext::fillPath() {
+ SkPath* path = m_data->getPath();
+ if (paintingDisabled() || !path)
+ return;
+
+ switch (this->fillRule()) {
+ case RULE_NONZERO:
+ path->setFillType(SkPath::kWinding_FillType);
+ break;
+ case RULE_EVENODD:
+ path->setFillType(SkPath::kEvenOdd_FillType);
+ break;
+ }
+
+ SkPaint paint;
+ m_data->setup_paint_fill(&paint);
+
+ extactShader(&paint, m_common->state.fillColorSpace,
+ m_common->state.fillPattern.get(),
+ m_common->state.fillGradient.get(), spreadMethod());
+
+ GC2Canvas(this)->drawPath(*path, paint);
+}
+
+void GraphicsContext::strokePath() {
+ const SkPath* path = m_data->getPath();
+ if (paintingDisabled() || !path || strokeStyle() == NoStroke)
+ return;
+
+ SkPaint paint;
+ m_data->setup_paint_stroke(&paint, NULL);
+
+ extactShader(&paint, m_common->state.strokeColorSpace,
+ m_common->state.strokePattern.get(),
+ m_common->state.strokeGradient.get(), spreadMethod());
+
+ GC2Canvas(this)->drawPath(*path, paint);
+}
+
+void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
+{
+ /*
+ enum InterpolationQuality {
+ InterpolationDefault,
+ InterpolationNone,
+ InterpolationLow,
+ InterpolationMedium,
+ InterpolationHigh
+ };
+
+ TODO: record this, so we can know when to use bitmap-filtering when we draw
+ ... not sure how meaningful this will be given our playback model.
+
+ Certainly safe to do nothing for the present.
+ */
+}
+
+} // namespace WebCore
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc) {
+ return gc->platformContext()->mCanvas;
+}
+