From 5fc5e1f3883ecadd4fd00df28225a17d7d3a37e9 Mon Sep 17 00:00:00 2001 From: John Reck Date: Mon, 19 Mar 2012 13:10:25 -0700 Subject: Platform graphics refactor Push the bulk of the work to PlatformGraphicsContext PlatformGraphicsContext does not depend on having a GraphicsContext Get ImageAndroid::drawPattern off of mCanvas Get BitmapImage::draw off of mCanvas Get WebFrameView off of mCanvas Cleanup GradientAndroid and remove mCanvas usage Change-Id: I3c8ad10c030cbc384436463e197ca1c0a69d34eb --- Source/WebCore/platform/graphics/Gradient.h | 8 - .../WebCore/platform/graphics/GraphicsContext.cpp | 4 +- Source/WebCore/platform/graphics/GraphicsContext.h | 6 - .../platform/graphics/android/GradientAndroid.cpp | 63 +- .../graphics/android/GraphicsContextAndroid.cpp | 938 +++--------------- .../platform/graphics/android/ImageAndroid.cpp | 74 +- .../graphics/android/PlatformGraphicsContext.cpp | 1011 +++++++++++++++++++- .../graphics/android/PlatformGraphicsContext.h | 111 ++- 8 files changed, 1272 insertions(+), 943 deletions(-) (limited to 'Source/WebCore/platform/graphics') diff --git a/Source/WebCore/platform/graphics/Gradient.h b/Source/WebCore/platform/graphics/Gradient.h index 7595896..ec22efe 100644 --- a/Source/WebCore/platform/graphics/Gradient.h +++ b/Source/WebCore/platform/graphics/Gradient.h @@ -58,14 +58,9 @@ typedef QGradient* PlatformGradient; typedef struct _cairo_pattern cairo_pattern_t; typedef cairo_pattern_t* PlatformGradient; #elif USE(SKIA) -#if PLATFORM(ANDROID) -#include "SkShader.h" -typedef class PlatformGradientRec* PlatformGradient; -#else class SkShader; typedef class SkShader* PlatformGradient; typedef class SkShader* PlatformPattern; -#endif #elif PLATFORM(HAIKU) class BGradient; typedef BGradient* PlatformGradient; @@ -116,9 +111,6 @@ namespace WebCore { #if OS(WINCE) && !PLATFORM(QT) const Vector& getStops() const; #else -#if PLATFORM(ANDROID) - SkShader* getShader(SkShader::TileMode); -#endif PlatformGradient platformGradient(); #endif diff --git a/Source/WebCore/platform/graphics/GraphicsContext.cpp b/Source/WebCore/platform/graphics/GraphicsContext.cpp index e032714..d8ea160 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext.cpp +++ b/Source/WebCore/platform/graphics/GraphicsContext.cpp @@ -662,7 +662,7 @@ CompositeOperator GraphicsContext::compositeOperation() const return m_state.compositeOperator; } -#if !(USE(SKIA) && !PLATFORM(ANDROID)) +#if !USE(SKIA) void GraphicsContext::setPlatformFillGradient(Gradient*) { } @@ -688,7 +688,7 @@ void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode) } #endif -#if !PLATFORM(QT) && !USE(CAIRO) && !(USE(SKIA) && !PLATFORM(ANDROID)) && !PLATFORM(HAIKU) && !PLATFORM(OPENVG) +#if !PLATFORM(QT) && !USE(CAIRO) && !USE(SKIA) && !PLATFORM(OPENVG) void GraphicsContext::setPlatformStrokeStyle(StrokeStyle) { } diff --git a/Source/WebCore/platform/graphics/GraphicsContext.h b/Source/WebCore/platform/graphics/GraphicsContext.h index ed43cf0..5c60e98 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext.h +++ b/Source/WebCore/platform/graphics/GraphicsContext.h @@ -287,8 +287,6 @@ namespace WebCore { #endif #if PLATFORM(ANDROID) - // initialize a paint for bitmaps - void setupBitmapPaint(SkPaint*); // initialize a paint for filling void setupFillPaint(SkPaint*); // initialize a paint for stroking @@ -301,10 +299,6 @@ namespace WebCore { // returns true if there is a valid (non-transparent) stroke color bool willStroke() const; - // may return NULL, since we lazily allocate the path. This is the path - // that is drawn by drawPath() - const SkPath* getCurrPath() const; - /** platform-specific factory method to return a bitmap graphicscontext, called by when we need to draw offscreen. Caller is responsible for deleting the context. Use drawOffscreenContext() to draw the context's image diff --git a/Source/WebCore/platform/graphics/android/GradientAndroid.cpp b/Source/WebCore/platform/graphics/android/GradientAndroid.cpp index 6007a0a..7bc69c5 100644 --- a/Source/WebCore/platform/graphics/android/GradientAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/GradientAndroid.cpp @@ -26,7 +26,6 @@ #include "config.h" #include "Gradient.h" -#include "android_graphics.h" #include "CSSParser.h" #include "GraphicsContext.h" #include "NotImplemented.h" @@ -35,16 +34,6 @@ #include "SkGradientShader.h" #include "SkPaint.h" -class PlatformGradientRec { -public: - PlatformGradientRec() : m_shader(NULL) {} - ~PlatformGradientRec() { SkSafeUnref(m_shader); } - - SkShader* m_shader; - SkShader::TileMode m_tileMode; - int m_colorCountWhenShaderWasBuilt; -}; - namespace WebCore { void Gradient::platformDestroy() @@ -58,12 +47,10 @@ static U8CPU F2B(float x) return (int)(x * 255); } -SkShader* Gradient::getShader(SkShader::TileMode mode) +SkShader* Gradient::platformGradient() { - if (NULL == m_gradient) - m_gradient = new PlatformGradientRec; - else if (mode == m_gradient->m_tileMode) - return m_gradient->m_shader; + if (m_gradient) + return m_gradient; // need to ensure that the m_stops array is sorted. We call getColor() // which, as a side effect, does the sort. @@ -73,6 +60,19 @@ SkShader* Gradient::getShader(SkShader::TileMode mode) this->getColor(0, &r, &g, &b, &a); } + SkShader::TileMode mode = SkShader::kClamp_TileMode; + switch (m_spreadMethod) { + case SpreadMethodReflect: + mode = SkShader::kMirror_TileMode; + break; + case SpreadMethodRepeat: + mode = SkShader::kRepeat_TileMode; + break; + case SpreadMethodPad: + mode = SkShader::kClamp_TileMode; + break; + } + SkPoint pts[2] = { m_p0, m_p1 }; // convert to SkPoint const size_t count = m_stops.size(); @@ -88,39 +88,28 @@ SkShader* Gradient::getShader(SkShader::TileMode mode) ++iter; } - SkShader* s; - if (m_radial) - s = SkGradientShader::CreateTwoPointRadial(pts[0], + if (m_radial) { + m_gradient = SkGradientShader::CreateTwoPointRadial(pts[0], SkFloatToScalar(m_r0), pts[1], SkFloatToScalar(m_r1), colors, pos, count, mode); - else - s = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); + } else + m_gradient = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); - if (NULL == s) - s = new SkColorShader(0); + if (!m_gradient) + m_gradient = new SkColorShader(0); - // zap our previous shader, if present - SkSafeUnref(m_gradient->m_shader); - m_gradient->m_shader = s; - m_gradient->m_tileMode = mode; SkMatrix matrix = m_gradientSpaceTransformation; - s->setLocalMatrix(matrix); + m_gradient->setLocalMatrix(matrix); - return s; + return m_gradient; } void Gradient::fill(GraphicsContext* context, const FloatRect& rect) { - SkPaint paint; - // we don't care about the mode, so try to use the existing one - SkShader::TileMode mode = m_gradient ? m_gradient->m_tileMode : - SkShader::kClamp_TileMode; - - paint.setAntiAlias(true); - paint.setShader(this->getShader(mode)); - android_gc2canvas(context)->drawRect(rect, paint); + context->setFillGradient(this); + context->fillRect(rect); } diff --git a/Source/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/Source/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp index 0aa1ae6..bdc8005 100644 --- a/Source/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp @@ -49,376 +49,36 @@ using namespace std; -#define GC2CANVAS(ctx) (ctx)->m_data->getPlatformGfxCtx()->mCanvas - namespace WebCore { -static int RoundToInt(float x) -{ - return (int)roundf(x); -} - -template 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; -} - -// TODO / questions - -// alpha: how does this interact with the alpha in Color? multiply them together? -// mode: 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 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) - {}; -}; - +// This class just holds onto a PlatformContextSkia for GraphicsContext. class GraphicsContextPlatformPrivate { + WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); public: - struct 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; - SkColor strokeColor; - bool useAA; - - 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) - , strokeColor(SK_ColorBLACK) - , useAA(true) - { - } - - 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) - , strokeColor(other.strokeColor) - , useAA(other.useAA) - { - SkSafeRef(pathEffect); - } - - ~State() - { - SkSafeUnref(pathEffect); - } - - 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(GraphicsContext* ctx, SkPaint* paint, SkPoint* offset) - { - 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 (ctx->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); - } - }; + GraphicsContextPlatformPrivate(PlatformGraphicsContext* platformContext) + : m_context(platformContext) { } + + PlatformGraphicsContext* context() { return m_context; } - GraphicsContextPlatformPrivate(GraphicsContext* gfxCtx, PlatformGraphicsContext* platformGfxCtx) - : m_parentGfxCtx(gfxCtx) - , m_platformGfxCtx(platformGfxCtx) - , m_stateStack(sizeof(State)) - { - State* state = static_cast(m_stateStack.push_back()); - new (state) State(); - m_state = state; - } - - ~GraphicsContextPlatformPrivate() - { - // We force restores so we don't leak any subobjects owned by our - // stack of State records. - while (m_stateStack.count() > 0) - this->restore(); - - if (m_platformGfxCtx && m_platformGfxCtx->deleteUs()) - delete m_platformGfxCtx; - } - - void save() - { - State* newState = static_cast(m_stateStack.push_back()); - new (newState) State(*m_state); - m_state = newState; - } - - void restore() - { - m_state->~State(); - m_stateStack.pop_back(); - m_state = static_cast(m_stateStack.back()); - } - - void setFillColor(const Color& c) - { - m_state->fillColor = c.rgb(); - } - - void setStrokeColor(const Color& c) - { - m_state->strokeColor = c.rgb(); - } - - void setStrokeThickness(float f) - { - m_state->strokeThickness = f; - } - - void 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 (m_parentGfxCtx->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(); - } - } - - void setupPaintFill(SkPaint* paint) const - { - this->setupPaintCommon(paint); - paint->setColor(m_state->applyAlpha(m_state->fillColor)); - } - - void setupPaintBitmap(SkPaint* paint) const - { - this->setupPaintCommon(paint); - // We only want the global alpha for bitmaps, - // so just give applyAlpha opaque black - paint->setColor(m_state->applyAlpha(0xFF000000)); - } - - // 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 setupPaintStroke(SkPaint* paint, SkRect* rect, bool isHLine = false) - { - this->setupPaintCommon(paint); - paint->setColor(m_state->applyAlpha(m_state->strokeColor)); - - 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_parentGfxCtx->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; - } - - PlatformGraphicsContext* getPlatformGfxCtx() - { - return m_platformGfxCtx; - } - - State* getState() - { - return m_state; - } private: - State* m_state; - GraphicsContext* m_parentGfxCtx; // Back-ptr to our parent - PlatformGraphicsContext* m_platformGfxCtx; - SkDeque m_stateStack; - // Not supported yet - State& operator=(const State&); + // Non-owning pointer to the PlatformContext. + PlatformGraphicsContext* m_context; }; -static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm) +static SkShader* extractShader(Pattern* pat, Gradient* grad) { - 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, Pattern* pat, Gradient* grad) -{ - if (pat) { - // platformPattern() returns a cached obj - paint->setShader(pat->platformPattern(AffineTransform())); - } else if (grad) { - // grad->getShader() returns a cached obj - GradientSpreadMethod sm = grad->spreadMethod(); - paint->setShader(grad->getShader(SpreadMethod2TileMode(sm))); - } + if (pat) + return pat->platformPattern(AffineTransform()); + else if (grad) + return grad->platformGradient(); + return 0; } //////////////////////////////////////////////////////////////////////////////////////////////// GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height) { - PlatformGraphicsContext* pgc = new PlatformGraphicsContext(); + PlatformGraphicsContext* pgc = new PlatformGraphicsContext(new SkCanvas, true); SkBitmap bitmap; @@ -435,7 +95,9 @@ GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height) void GraphicsContext::platformInit(PlatformGraphicsContext* gc) { - m_data = new GraphicsContextPlatformPrivate(this, gc); + if (gc) + gc->setGraphicsContext(this); + m_data = new GraphicsContextPlatformPrivate(gc); setPaintingDisabled(!gc || !gc->mCanvas); } @@ -446,28 +108,22 @@ void GraphicsContext::platformDestroy() void GraphicsContext::savePlatformState() { - // Save our private State - m_data->save(); - // Save our native canvas - GC2CANVAS(this)->save(); + platformContext()->save(); } void GraphicsContext::restorePlatformState() { - // Restore our private State - m_data->restore(); - // Restore our native canvas - GC2CANVAS(this)->restore(); + platformContext()->restore(); } bool GraphicsContext::willFill() const { - return m_data->getState()->fillColor; + return m_state.fillColor.rgb(); } bool GraphicsContext::willStroke() const { - return m_data->getState()->strokeColor; + return m_state.strokeColor.rgb(); } // Draws a filled rectangle with a stroked border. @@ -476,24 +132,7 @@ void GraphicsContext::drawRect(const IntRect& rect) if (paintingDisabled()) return; - SkPaint paint; - SkRect r(rect); - - if (fillColor().alpha()) { - m_data->setupPaintFill(&paint); - GC2CANVAS(this)->drawRect(r, paint); - } - - // According to GraphicsContext.h, stroking inside drawRect always means - // a stroke of 1 inside the rect. - if (strokeStyle() != NoStroke && strokeColor().alpha()) { - paint.reset(); - m_data->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" - GC2CANVAS(this)->drawRect(r, paint); - } + platformContext()->drawRect(rect); } // This is only used to draw borders. @@ -502,119 +141,24 @@ 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->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); - } -} - -static void setrectForUnderline(SkRect* r, GraphicsContext* context, const FloatPoint& point, int yOffset, float width) -{ - float lineThickness = context->strokeThickness(); -#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; + platformContext()->drawLine(point1, point2); } -void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool) +void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool /* printing */) { if (paintingDisabled()) return; - SkRect r; - setrectForUnderline(&r, this, pt, 0, width); - - SkPaint paint; - paint.setAntiAlias(true); - paint.setColor(this->strokeColor().rgb()); - - GC2CANVAS(this)->drawRect(r, paint); + platformContext()->drawLineForText(pt, width); } -// TODO: Should we draw different based on TextCheckingLineStyle? -void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, TextCheckingLineStyle) +void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, + TextCheckingLineStyle style) { if (paintingDisabled()) return; - SkRect r; - setrectForUnderline(&r, this, pt, 0, width); - - SkPaint paint; - paint.setAntiAlias(true); - paint.setColor(SK_ColorRED); // Is this specified somewhere? - - GC2CANVAS(this)->drawRect(r, paint); + platformContext()->drawLineForTextChecking(pt, width, style); } // This method is only used to draw the little circles used in lists. @@ -623,28 +167,7 @@ void GraphicsContext::drawEllipse(const IntRect& rect) if (paintingDisabled()) return; - SkPaint paint; - SkRect oval(rect); - - if (fillColor().rgb() & 0xFF000000) { - m_data->setupPaintFill(&paint); - GC2CANVAS(this)->drawOval(oval, paint); - } - if (strokeStyle() != NoStroke) { - paint.reset(); - m_data->setupPaintStroke(&paint, &oval); - GC2CANVAS(this)->drawOval(oval, paint); - } -} - -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); + platformContext()->drawEllipse(rect); } void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) @@ -652,138 +175,43 @@ void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) if (paintingDisabled()) return; - SkPath path; - SkPaint paint; - SkRect oval(r); - - if (strokeStyle() == NoStroke) { - m_data->setupPaintFill(&paint); // We want the fill color - paint.setStyle(SkPaint::kStroke_Style); - paint.setStrokeWidth(SkFloatToScalar(this->strokeThickness())); - } else - m_data->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)); - GC2CANVAS(this)->drawPath(path, paint); + platformContext()->strokeArc(r, startAngle, angleSpan); } -void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias) +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->setupPaintFill(&paint); - paint.setAntiAlias(shouldAntialias); - GC2CANVAS(this)->drawPath(path, paint); - } - - if (strokeStyle() != NoStroke) { - paint.reset(); - m_data->setupPaintStroke(&paint, 0); - paint.setAntiAlias(shouldAntialias); - GC2CANVAS(this)->drawPath(path, paint); - } + platformContext()->drawConvexPolygon(numPoints, points, shouldAntialias); } void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, - const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace) + const IntSize& bottomLeft, const IntSize& bottomRight, + const Color& color, ColorSpace colorSpace) { if (paintingDisabled()) return; - 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); - - m_data->setupPaintFill(&paint); - paint.setColor(color.rgb()); - GC2CANVAS(this)->drawPath(path, paint); + platformContext()->fillRoundedRect(rect, topLeft, topRight, + bottomLeft, bottomRight, color, colorSpace); } void GraphicsContext::fillRect(const FloatRect& rect) { - save(); - SkPaint paint; - - m_data->setupPaintFill(&paint); - - extactShader(&paint, - m_state.fillPattern.get(), - m_state.fillGradient.get()); + if (paintingDisabled()) + return; - GC2CANVAS(this)->drawRect(rect, paint); - restore(); + platformContext()->fillRect(rect); } -void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace) +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) { if (paintingDisabled()) return; - if (color.rgb() & 0xFF000000) { - save(); - SkPaint paint; - - m_data->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); - - GC2CANVAS(this)->drawRect(rect, paint); - restore(); - } + platformContext()->fillRect(rect, color, colorSpace); } void GraphicsContext::clip(const FloatRect& rect) @@ -791,7 +219,7 @@ void GraphicsContext::clip(const FloatRect& rect) if (paintingDisabled()) return; - GC2CANVAS(this)->clipRect(rect); + platformContext()->clip(rect); } void GraphicsContext::clip(const Path& path) @@ -799,7 +227,7 @@ void GraphicsContext::clip(const Path& path) if (paintingDisabled()) return; - GC2CANVAS(this)->clipPath(*path.platformPath(), SkRegion::kIntersect_Op, true); + platformContext()->clip(path); } void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) @@ -807,24 +235,15 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness if (paintingDisabled()) return; - 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); - } - GC2CANVAS(this)->clipPath(path, SkRegion::kIntersect_Op, true); + platformContext()->addInnerRoundedRectClip(rect, thickness); } void GraphicsContext::canvasClip(const Path& path) { - clip(path); + if (paintingDisabled()) + return; + + platformContext()->canvasClip(path); } void GraphicsContext::clipOut(const IntRect& r) @@ -832,7 +251,7 @@ void GraphicsContext::clipOut(const IntRect& r) if (paintingDisabled()) return; - GC2CANVAS(this)->clipRect(r, SkRegion::kDifference_Op); + platformContext()->clipOut(r); } #if ENABLE(SVG) @@ -841,9 +260,7 @@ void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) if (paintingDisabled()) return; - SkPath path = *pathToClip.platformPath(); - path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); - GC2CANVAS(this)->clipPath(path); + platformContext()->clipPath(pathToClip, clipRule); } #endif @@ -852,7 +269,7 @@ void GraphicsContext::clipOut(const Path& p) if (paintingDisabled()) return; - GC2CANVAS(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op); + platformContext()->clipOut(p); } ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -864,20 +281,12 @@ KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext() } #endif -// 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) - void GraphicsContext::beginTransparencyLayer(float opacity) { if (paintingDisabled()) return; - SkCanvas* canvas = GC2CANVAS(this); - canvas->saveLayerAlpha(0, (int)(opacity * 255), TRANSPARENCY_SAVEFLAGS); + platformContext()->beginTransparencyLayer(opacity); } void GraphicsContext::endTransparencyLayer() @@ -885,44 +294,44 @@ void GraphicsContext::endTransparencyLayer() if (paintingDisabled()) return; - GC2CANVAS(this)->restore(); + platformContext()->endTransparencyLayer(); } /////////////////////////////////////////////////////////////////////////// -void GraphicsContext::setupBitmapPaint(SkPaint* paint) -{ - m_data->setupPaintBitmap(paint); -} - void GraphicsContext::setupFillPaint(SkPaint* paint) { - m_data->setupPaintFill(paint); + platformContext()->setupPaintFill(paint); } void GraphicsContext::setupStrokePaint(SkPaint* paint) { - m_data->setupPaintStroke(paint, 0); + platformContext()->setupPaintStroke(paint, 0); } bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset) { - return m_data->getState()->setupShadowPaint(this, paint, offset); + return platformContext()->setupPaintShadow(paint, offset); } void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace) { - m_data->setStrokeColor(c); + platformContext()->setStrokeColor(c); } void GraphicsContext::setPlatformStrokeThickness(float f) { - m_data->setStrokeThickness(f); + platformContext()->setStrokeThickness(f); +} + +void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style) +{ + platformContext()->setStrokeStyle(style); } void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace) { - m_data->setFillColor(c); + platformContext()->setFillColor(c); } void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace) @@ -938,7 +347,7 @@ void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const c = color.rgb(); else c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color - m_data->getState()->setShadow(blur, size.width(), size.height(), c); + platformContext()->setShadow(blur, size.width(), size.height(), c); } void GraphicsContext::clearPlatformShadow() @@ -946,38 +355,17 @@ void GraphicsContext::clearPlatformShadow() if (paintingDisabled()) return; - m_data->getState()->setShadow(0, 0, 0, 0); + platformContext()->setShadow(0, 0, 0, 0); } /////////////////////////////////////////////////////////////////////////////// -void GraphicsContext::drawFocusRing(const Vector& rects, int /* width */, int /* offset */, const Color& color) +void GraphicsContext::drawFocusRing(const Vector& rects, int width, int offset, const Color& color) { if (paintingDisabled()) return; - 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); - platformContext()->mCanvas->drawPath(path, paint); + platformContext()->drawFocusRing(rects, width, offset, color); } void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&) @@ -988,22 +376,28 @@ void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&) PlatformGraphicsContext* GraphicsContext::platformContext() const { ASSERT(!paintingDisabled()); - return m_data->getPlatformGfxCtx(); + return m_data->context(); } void GraphicsContext::setMiterLimit(float limit) { - m_data->getState()->miterLimit = limit; + if (paintingDisabled()) + return; + platformContext()->setMiterLimit(limit); } void GraphicsContext::setAlpha(float alpha) { - m_data->getState()->alpha = alpha; + if (paintingDisabled()) + return; + platformContext()->setAlpha(alpha); } void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) { - m_data->getState()->mode = WebCoreCompositeToSkiaComposite(op); + if (paintingDisabled()) + return; + platformContext()->setCompositeOperation(op); } void GraphicsContext::clearRect(const FloatRect& rect) @@ -1011,11 +405,7 @@ void GraphicsContext::clearRect(const FloatRect& rect) if (paintingDisabled()) return; - SkPaint paint; - - m_data->setupPaintFill(&paint); - paint.setXfermodeMode(SkXfermode::kClear_Mode); - GC2CANVAS(this)->drawRect(rect, paint); + platformContext()->clearRect(rect); } void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) @@ -1023,29 +413,14 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) if (paintingDisabled()) return; - SkPaint paint; - - m_data->setupPaintStroke(&paint, 0); - paint.setStrokeWidth(SkFloatToScalar(lineWidth)); - GC2CANVAS(this)->drawRect(rect, paint); + platformContext()->strokeRect(rect, lineWidth); } void GraphicsContext::setLineCap(LineCap cap) { - switch (cap) { - case ButtCap: - m_data->getState()->lineCap = SkPaint::kButt_Cap; - break; - case RoundCap: - m_data->getState()->lineCap = SkPaint::kRound_Cap; - break; - case SquareCap: - m_data->getState()->lineCap = SkPaint::kSquare_Cap; - break; - default: - SkDEBUGF(("GraphicsContext::setLineCap: unknown LineCap %d\n", cap)); - break; - } + if (paintingDisabled()) + return; + platformContext()->setLineCap(cap); } #if ENABLE(SVG) @@ -1054,67 +429,43 @@ void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) if (paintingDisabled()) return; - 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_data->getState()->pathEffect; - SkSafeUnref(*effectPtr); - *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset)); - - delete[] intervals; + platformContext()->setLineDash(dashes, dashOffset); } #endif void GraphicsContext::setLineJoin(LineJoin join) { - switch (join) { - case MiterJoin: - m_data->getState()->lineJoin = SkPaint::kMiter_Join; - break; - case RoundJoin: - m_data->getState()->lineJoin = SkPaint::kRound_Join; - break; - case BevelJoin: - m_data->getState()->lineJoin = SkPaint::kBevel_Join; - break; - default: - SkDEBUGF(("GraphicsContext::setLineJoin: unknown LineJoin %d\n", join)); - break; - } + if (paintingDisabled()) + return; + platformContext()->setLineJoin(join); } void GraphicsContext::scale(const FloatSize& size) { if (paintingDisabled()) return; - GC2CANVAS(this)->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height())); + platformContext()->scale(size); } void GraphicsContext::rotate(float angleInRadians) { if (paintingDisabled()) return; - GC2CANVAS(this)->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f))); + platformContext()->rotate(angleInRadians); } void GraphicsContext::translate(float x, float y) { if (paintingDisabled()) return; - GC2CANVAS(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y)); + platformContext()->translate(x, y); } void GraphicsContext::concatCTM(const AffineTransform& affine) { if (paintingDisabled()) return; - GC2CANVAS(this)->concat(affine); + platformContext()->concatCTM(affine); } // This is intended to round the rect to device pixels (through the CTM) @@ -1139,39 +490,42 @@ FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMo 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); +void GraphicsContext::setPlatformShouldAntialias(bool useAA) +{ + if (paintingDisabled()) + return; + platformContext()->setShouldAntialias(useAA); +} - CGPDFContextSetURLForRect(context, urlRef, - CGRectApplyAffineTransform(rect, CGContextGetCTM(context))); +void GraphicsContext::setPlatformFillGradient(Gradient* fillGradient) +{ + SkShader* shader = extractShader(0, fillGradient); + platformContext()->setFillShader(shader); +} - CFRelease(urlRef); +void GraphicsContext::setPlatformFillPattern(Pattern* fillPattern) +{ + SkShader* shader = extractShader(fillPattern, 0); + platformContext()->setFillShader(shader); } -#endif + +void GraphicsContext::setPlatformStrokeGradient(Gradient* strokeGradient) +{ + SkShader* shader = extractShader(0, strokeGradient); + platformContext()->setStrokeShader(shader); } -void GraphicsContext::setPlatformShouldAntialias(bool useAA) +void GraphicsContext::setPlatformStrokePattern(Pattern* strokePattern) { - if (paintingDisabled()) - return; - m_data->getState()->useAA = useAA; + SkShader* shader = extractShader(strokePattern, 0); + platformContext()->setStrokeShader(shader); } AffineTransform GraphicsContext::getCTM() const { - const SkMatrix& m = GC2CANVAS(this)->getTotalMatrix(); + const SkMatrix& m = platformContext()->getTotalMatrix(); return AffineTransform(SkScalarToDouble(m.getScaleX()), // a SkScalarToDouble(m.getSkewY()), // b SkScalarToDouble(m.getSkewX()), // c @@ -1193,43 +547,18 @@ void GraphicsContext::setCTM(const AffineTransform& transform) void GraphicsContext::fillPath(const Path& pathToFill) { - SkPath* path = pathToFill.platformPath(); - if (paintingDisabled() || !path) + if (paintingDisabled()) 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->setupPaintFill(&paint); - - extactShader(&paint, - m_state.fillPattern.get(), - m_state.fillGradient.get()); - - GC2CANVAS(this)->drawPath(*path, paint); + platformContext()->fillPath(pathToFill, fillRule()); } void GraphicsContext::strokePath(const Path& pathToStroke) { - const SkPath* path = pathToStroke.platformPath(); - if (paintingDisabled() || !path) + if (paintingDisabled()) return; - SkPaint paint; - m_data->setupPaintStroke(&paint, 0); - - extactShader(&paint, - m_state.strokePattern.get(), - m_state.strokeGradient.get()); - - GC2CANVAS(this)->drawPath(*path, paint); + platformContext()->strokePath(pathToStroke); } InterpolationQuality GraphicsContext::imageInterpolationQuality() const @@ -1255,7 +584,8 @@ void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode) // Certainly safe to do nothing for the present. } -void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias) +void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, + bool antialias) { if (paintingDisabled()) return; @@ -1266,23 +596,17 @@ void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, boo // FIXME: IMPLEMENT! } -void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to, bool isActive) +void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, + const FloatPoint& point, int h, + const Color& backgroundColor, + ColorSpace colorSpace, int from, + int to, bool isActive) { if (paintingDisabled()) return; - 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); - } + platformContext()->drawHighlightForText(font, run, point, h, backgroundColor, + colorSpace, from, to, isActive); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/ImageAndroid.cpp b/Source/WebCore/platform/graphics/android/ImageAndroid.cpp index 8e0c112..08f72e0 100644 --- a/Source/WebCore/platform/graphics/android/ImageAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/ImageAndroid.cpp @@ -172,20 +172,7 @@ static void round_scaled(SkIRect* dst, const WebCore::FloatRect& src, SkScalarRound(SkFloatToScalar((src.y() + src.height()) * sy))); } -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); -} - -void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, +void BitmapImage::draw(GraphicsContext* gc, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp) { @@ -222,14 +209,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, return; } - SkCanvas* canvas = ctxt->platformContext()->mCanvas; - SkPaint paint; - - ctxt->setupBitmapPaint(&paint); // need global alpha among other things - paint.setFilterBitmap(true); - paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); - fixPaintForBitmapsThatMaySeam(&paint); - canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint); + gc->platformContext()->drawBitmapRect(bitmap, &srcR, dstR, compositeOp); #ifdef TRACE_SUBSAMPLED_BITMAPS if (bitmap.width() != image->origWidth() || @@ -248,26 +228,19 @@ void BitmapImage::setURL(const String& str) /////////////////////////////////////////////////////////////////////////////// -void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, +void Image::drawPattern(GraphicsContext* gc, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace, CompositeOperator compositeOp, const FloatRect& destRect) { SkBitmapRef* image = this->nativeImageForCurrentFrame(); - if (!image) { // If it's too early we won't have an image yet. + if (!image || destRect.isEmpty()) return; - } // in case we get called with an incomplete bitmap const SkBitmap& origBitmap = image->bitmap(); - if (origBitmap.getPixels() == NULL && origBitmap.pixelRef() == NULL) { + if (origBitmap.getPixels() == NULL && origBitmap.pixelRef() == NULL) return; - } - - SkRect dstR(destRect); - if (dstR.isEmpty()) { - return; - } SkIRect srcR; // we may have to scale if the image has been subsampled (so save RAM) @@ -278,11 +251,9 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, if (imageIsSubSampled) { scaleX = (float)image->origWidth() / origBitmap.width(); scaleY = (float)image->origHeight() / origBitmap.height(); -// SkDebugf("----- subsampled %g %g\n", scaleX, scaleY); round_scaled(&srcR, srcRect, 1 / scaleX, 1 / scaleY); - } else { + } else round(&srcR, srcRect); - } // now extract the proper subset of the src image SkBitmap bitmap; @@ -291,19 +262,6 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, return; } - SkCanvas* canvas = ctxt->platformContext()->mCanvas; - SkPaint paint; - ctxt->setupBitmapPaint(&paint); // need global alpha among other things - - SkShader* shader = SkShader::CreateBitmapShader(bitmap, - SkShader::kRepeat_TileMode, - SkShader::kRepeat_TileMode); - paint.setShader(shader)->unref(); - // now paint is the only owner of shader - paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); - paint.setFilterBitmap(true); - fixPaintForBitmapsThatMaySeam(&paint); - SkMatrix matrix(patternTransform); if (imageIsSubSampled) { @@ -316,26 +274,8 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, float tx = phase.x() + srcRect.x() * patternTransform.a(); float ty = phase.y() + srcRect.y() * patternTransform.d(); matrix.postTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty)); - shader->setLocalMatrix(matrix); -#if 0 - SkDebugf("--- drawPattern: src [%g %g %g %g] dst [%g %g %g %g] transform [%g %g %g %g %g %g] matrix [%g %g %g %g %g %g]\n", - srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), - destRect.x(), destRect.y(), destRect.width(), destRect.height(), - patternTransform.a(), patternTransform.b(), patternTransform.c(), - patternTransform.d(), patternTransform.e(), patternTransform.f(), - matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); -#endif - canvas->drawRect(dstR, paint); -#ifdef TRACE_SUBSAMPLED_BITMAPS - if (bitmap.width() != image->origWidth() || - bitmap.height() != image->origHeight()) { - SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n", - bitmap.width(), bitmap.height(), - image->origWidth(), image->origHeight(), - SkScalarToFloat(dstR.width()), SkScalarToFloat(dstR.height())); - } -#endif + gc->platformContext()->drawBitmapPattern(bitmap, matrix, compositeOp, destRect); } // missingImage, textAreaResizeCorner 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 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& 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 diff --git a/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h index 80ea5d6..ce126a4 100644 --- a/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h +++ b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h @@ -27,30 +27,123 @@ #define platform_graphics_context_h #include "IntRect.h" +#include "GraphicsContext.h" #include "RenderSkinAndroid.h" #include "SkCanvas.h" #include "SkPicture.h" #include "SkTDArray.h" +#include class SkCanvas; namespace WebCore { - - class GraphicsContext; class PlatformGraphicsContext { public: - PlatformGraphicsContext(); - // Pass in a recording canvas, and an array of button information to be - // updated. - PlatformGraphicsContext(SkCanvas* canvas); + PlatformGraphicsContext(SkCanvas* canvas, bool takeCanvasOwnership = false); ~PlatformGraphicsContext(); - + + void setGraphicsContext(GraphicsContext* gc) { m_gc = gc; } + + // FIXME: Make mCanvas private SkCanvas* mCanvas; - + // FIXME: This is used by ImageBufferAndroid, which should really be + // managing the canvas lifecycle itself bool deleteUs() const { return m_deleteCanvas; } + + // State management + void beginTransparencyLayer(float opacity); + void endTransparencyLayer(); + void save(); + void restore(); + + // State values + void setAlpha(float alpha); + void setCompositeOperation(CompositeOperator op); + void setFillColor(const Color& c); + void setFillShader(SkShader* fillShader); + void setLineCap(LineCap cap); + void setLineDash(const DashArray& dashes, float dashOffset); + void setLineJoin(LineJoin join); + void setMiterLimit(float limit); + void setShadow(int radius, int dx, int dy, SkColor c); + void setShouldAntialias(bool useAA); + void setStrokeColor(const Color& c); + void setStrokeShader(SkShader* strokeShader); + void setStrokeStyle(StrokeStyle style); + void setStrokeThickness(float f); + + // FIXME: These setupPaint* should be private, but + // they are used by FontAndroid currently + void setupPaintFill(SkPaint* paint) const; + bool setupPaintShadow(SkPaint* paint, SkPoint* offset) const; + // 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 setupPaintStroke(SkPaint* paint, SkRect* rect, bool isHLine = false); + + // Matrix operations + void concatCTM(const AffineTransform& affine); + void rotate(float angleInRadians); + void scale(const FloatSize& size); + void translate(float x, float y); + const SkMatrix& getTotalMatrix() { return mCanvas->getTotalMatrix(); } + + // Clipping + void addInnerRoundedRectClip(const IntRect& rect, int thickness); + void canvasClip(const Path& path); + void clip(const FloatRect& rect); + void clip(const Path& path); + void clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias); + void clipOut(const IntRect& r); + void clipOut(const Path& p); + void clipPath(const Path& pathToClip, WindRule clipRule); + + // Drawing + void clearRect(const FloatRect& rect); + void drawBitmapPattern(const SkBitmap& bitmap, const SkMatrix& matrix, + CompositeOperator compositeOp, const FloatRect& destRect); + void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src, + const SkRect& dst, CompositeOperator op); + void drawConvexPolygon(size_t numPoints, const FloatPoint* points, + bool shouldAntialias); + void drawEllipse(const IntRect& rect); + void drawFocusRing(const Vector& rects, int /* width */, + int /* offset */, const Color& color); + void drawHighlightForText(const Font& font, const TextRun& run, + const FloatPoint& point, int h, + const Color& backgroundColor, ColorSpace colorSpace, + int from, int to, bool isActive); + void drawLine(const IntPoint& point1, const IntPoint& point2); + void drawLineForText(const FloatPoint& pt, float width); + void drawLineForTextChecking(const FloatPoint& pt, float width, + GraphicsContext::TextCheckingLineStyle); + void drawRect(const IntRect& rect); + void fillPath(const Path& pathToFill, WindRule fillRule); + void fillRect(const FloatRect& rect); + void fillRect(const FloatRect& rect, const Color& color, ColorSpace); + void fillRoundedRect(const IntRect& rect, const IntSize& topLeft, + const IntSize& topRight, const IntSize& bottomLeft, + const IntSize& bottomRight, const Color& color, + ColorSpace); + void strokeArc(const IntRect& r, int startAngle, int angleSpan); + void strokePath(const Path& pathToStroke); + void strokeRect(const FloatRect& rect, float lineWidth); + private: - bool m_deleteCanvas; + + // shadowsIgnoreTransforms is only true for canvas's ImageBuffer, which will + // have a GraphicsContext + bool shadowsIgnoreTransforms() const { + return m_gc && m_gc->shadowsIgnoreTransforms(); + } + + void setupPaintCommon(SkPaint* paint) const; + + bool m_deleteCanvas; + struct State; + WTF::Vector m_stateStack; + State* m_state; + GraphicsContext* m_gc; // Back-ptr to our parent }; } -- cgit v1.1