diff options
Diffstat (limited to 'WebCore/platform/graphics/android/GraphicsContextAndroid.cpp')
-rw-r--r-- | WebCore/platform/graphics/android/GraphicsContextAndroid.cpp | 1118 |
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; +} + |