blob: e69c1451ad41733672310f97bb3980f0288f0611 [file] [log] [blame]
/*
* 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 THE COPYRIGHT OWNER 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 "GraphicsContext.h"
#include "AffineTransform.h"
#include "Font.h"
#include "Gradient.h"
#include "NotImplemented.h"
#include "Path.h"
#include "Pattern.h"
#include "PlatformGraphicsContext.h"
#include "PlatformGraphicsContextSkia.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"
using namespace std;
namespace WebCore {
// This class just holds onto a PlatformContextSkia for GraphicsContext.
class GraphicsContextPlatformPrivate {
WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate);
public:
GraphicsContextPlatformPrivate(PlatformGraphicsContext* platformContext)
: m_context(platformContext) { }
~GraphicsContextPlatformPrivate()
{
if (m_context && m_context->deleteUs())
delete m_context;
}
PlatformGraphicsContext* context() { return m_context; }
private:
// Non-owning pointer to the PlatformContext.
PlatformGraphicsContext* m_context;
};
static void syncPlatformContext(GraphicsContext* gc)
{
// Stroke and fill sometimes reference each other, so always
// sync them both to make sure our state is consistent.
PlatformGraphicsContext* pgc = gc->platformContext();
Gradient* grad = gc->state().fillGradient.get();
Pattern* pat = gc->state().fillPattern.get();
if (grad)
pgc->setFillShader(grad->platformGradient());
else if (pat)
pgc->setFillShader(pat->platformPattern(AffineTransform()));
else
pgc->setFillColor(gc->state().fillColor);
grad = gc->state().strokeGradient.get();
pat = gc->state().strokePattern.get();
if (grad)
pgc->setStrokeShader(grad->platformGradient());
else if (pat)
pgc->setStrokeShader(pat->platformPattern(AffineTransform()));
else
pgc->setStrokeColor(gc->state().strokeColor);
}
////////////////////////////////////////////////////////////////////////////////////////////////
GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height)
{
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
bitmap.allocPixels();
bitmap.eraseColor(0);
PlatformGraphicsContextSkia* pgc =
new PlatformGraphicsContextSkia(new SkCanvas(bitmap), true);
GraphicsContext* ctx = new GraphicsContext(pgc);
return ctx;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
{
if (gc)
gc->setGraphicsContext(this);
m_data = new GraphicsContextPlatformPrivate(gc);
setPaintingDisabled(!gc || gc->isPaintingDisabled());
}
void GraphicsContext::platformDestroy()
{
delete m_data;
}
void GraphicsContext::savePlatformState()
{
if (paintingDisabled())
return;
platformContext()->save();
}
void GraphicsContext::restorePlatformState()
{
if (paintingDisabled())
return;
platformContext()->restore();
}
bool GraphicsContext::willFill() const
{
return m_state.fillColor.rgb();
}
bool GraphicsContext::willStroke() const
{
return m_state.strokeColor.rgb();
}
// Draws a filled rectangle with a stroked border.
void GraphicsContext::drawRect(const IntRect& rect)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->drawRect(rect);
}
// This is only used to draw borders.
void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->drawLine(point1, point2);
}
void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool /* printing */)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->drawLineForText(pt, width);
}
void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width,
TextCheckingLineStyle style)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->drawLineForTextChecking(pt, width, style);
}
// This method is only used to draw the little circles used in lists.
void GraphicsContext::drawEllipse(const IntRect& rect)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->drawEllipse(rect);
}
void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->strokeArc(r, startAngle, angleSpan);
}
void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points,
bool shouldAntialias)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
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 colorSpace)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->fillRoundedRect(rect, topLeft, topRight,
bottomLeft, bottomRight, color, colorSpace);
}
void GraphicsContext::fillRect(const FloatRect& rect)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->fillRect(rect);
}
void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->fillRect(rect, color, colorSpace);
}
void GraphicsContext::clip(const FloatRect& rect)
{
if (paintingDisabled())
return;
platformContext()->clip(rect);
}
void GraphicsContext::clip(const Path& path)
{
if (paintingDisabled())
return;
platformContext()->clip(path);
}
void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
{
if (paintingDisabled())
return;
platformContext()->addInnerRoundedRectClip(rect, thickness);
}
void GraphicsContext::canvasClip(const Path& path)
{
if (paintingDisabled())
return;
platformContext()->canvasClip(path);
}
void GraphicsContext::clipOut(const IntRect& r)
{
if (paintingDisabled())
return;
platformContext()->clipOut(r);
}
#if ENABLE(SVG)
void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
{
if (paintingDisabled())
return;
platformContext()->clipPath(pathToClip, clipRule);
}
#endif
void GraphicsContext::clipOut(const Path& p)
{
if (paintingDisabled())
return;
platformContext()->clipOut(p);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
#if SVG_SUPPORT
KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
{
return new KRenderingDeviceContextQuartz(platformContext());
}
#endif
void GraphicsContext::beginTransparencyLayer(float opacity)
{
if (paintingDisabled())
return;
platformContext()->beginTransparencyLayer(opacity);
}
void GraphicsContext::endTransparencyLayer()
{
if (paintingDisabled())
return;
platformContext()->endTransparencyLayer();
}
///////////////////////////////////////////////////////////////////////////
void GraphicsContext::setupFillPaint(SkPaint* paint)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->setupPaintFill(paint);
}
void GraphicsContext::setupStrokePaint(SkPaint* paint)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->setupPaintStroke(paint, 0);
}
bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset)
{
if (paintingDisabled())
return false;
syncPlatformContext(this);
return platformContext()->setupPaintShadow(paint, offset);
}
void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace)
{
}
void GraphicsContext::setPlatformStrokeThickness(float f)
{
if (paintingDisabled())
return;
platformContext()->setStrokeThickness(f);
}
void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style)
{
if (paintingDisabled())
return;
platformContext()->setStrokeStyle(style);
}
void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace)
{
}
void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
{
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
platformContext()->setShadow(blur, size.width(), size.height(), c);
}
void GraphicsContext::clearPlatformShadow()
{
if (paintingDisabled())
return;
platformContext()->setShadow(0, 0, 0, 0);
}
///////////////////////////////////////////////////////////////////////////////
void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->drawFocusRing(rects, width, offset, color);
}
void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&)
{
// Do nothing, since we draw the focus ring independently.
}
PlatformGraphicsContext* GraphicsContext::platformContext() const
{
ASSERT(!paintingDisabled());
return m_data->context();
}
void GraphicsContext::setMiterLimit(float limit)
{
if (paintingDisabled())
return;
platformContext()->setMiterLimit(limit);
}
void GraphicsContext::setAlpha(float alpha)
{
if (paintingDisabled())
return;
platformContext()->setAlpha(alpha);
}
void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
{
if (paintingDisabled())
return;
platformContext()->setCompositeOperation(op);
}
void GraphicsContext::clearRect(const FloatRect& rect)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->clearRect(rect);
}
void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->strokeRect(rect, lineWidth);
}
void GraphicsContext::setLineCap(LineCap cap)
{
if (paintingDisabled())
return;
platformContext()->setLineCap(cap);
}
#if ENABLE(SVG)
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
{
if (paintingDisabled())
return;
platformContext()->setLineDash(dashes, dashOffset);
}
#endif
void GraphicsContext::setLineJoin(LineJoin join)
{
if (paintingDisabled())
return;
platformContext()->setLineJoin(join);
}
void GraphicsContext::scale(const FloatSize& size)
{
if (paintingDisabled())
return;
platformContext()->scale(size);
}
void GraphicsContext::rotate(float angleInRadians)
{
if (paintingDisabled())
return;
platformContext()->rotate(angleInRadians);
}
void GraphicsContext::translate(float x, float y)
{
if (paintingDisabled())
return;
if (!x && !y)
return;
platformContext()->translate(x, y);
}
void GraphicsContext::concatCTM(const AffineTransform& affine)
{
if (paintingDisabled())
return;
platformContext()->concatCTM(affine);
}
// This is intended to round the rect to device pixels (through the CTM)
// and then invert the result back into source space, with the hope that when
// it is drawn (through the matrix), it will land in the "right" place (i.e.
// on pixel boundaries).
// For android, we record this geometry once and then draw it though various
// scale factors as the user zooms, without re-recording. Thus this routine
// should just leave the original geometry alone.
// If we instead draw into bitmap tiles, we should then perform this
// transform -> round -> inverse step.
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
{
return rect;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
{
// Appears to be PDF specific, so we ignore it
}
void GraphicsContext::setPlatformShouldAntialias(bool useAA)
{
if (paintingDisabled())
return;
platformContext()->setShouldAntialias(useAA);
}
void GraphicsContext::setPlatformFillGradient(Gradient* fillGradient)
{
}
void GraphicsContext::setPlatformFillPattern(Pattern* fillPattern)
{
}
void GraphicsContext::setPlatformStrokeGradient(Gradient* strokeGradient)
{
}
void GraphicsContext::setPlatformStrokePattern(Pattern* strokePattern)
{
}
AffineTransform GraphicsContext::getCTM() const
{
if (paintingDisabled())
return AffineTransform();
const SkMatrix& m = platformContext()->getTotalMatrix();
return AffineTransform(SkScalarToDouble(m.getScaleX()), // a
SkScalarToDouble(m.getSkewY()), // b
SkScalarToDouble(m.getSkewX()), // c
SkScalarToDouble(m.getScaleY()), // d
SkScalarToDouble(m.getTranslateX()), // e
SkScalarToDouble(m.getTranslateY())); // f
}
void GraphicsContext::setCTM(const AffineTransform& transform)
{
// The SkPicture mode of Skia does not support SkCanvas::setMatrix(), so we
// can not simply use that method here. We could calculate the transform
// required to achieve the desired matrix and use SkCanvas::concat(), but
// there's currently no need for this.
ASSERT_NOT_REACHED();
}
///////////////////////////////////////////////////////////////////////////////
void GraphicsContext::fillPath(const Path& pathToFill)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->fillPath(pathToFill, fillRule());
}
void GraphicsContext::strokePath(const Path& pathToStroke)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->strokePath(pathToStroke);
}
InterpolationQuality GraphicsContext::imageInterpolationQuality() const
{
notImplemented();
return InterpolationDefault;
}
void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
{
#if 0
enum InterpolationQuality {
InterpolationDefault,
InterpolationNone,
InterpolationLow,
InterpolationMedium,
InterpolationHigh
};
#endif
// 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.
}
void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*,
bool antialias)
{
if (paintingDisabled())
return;
if (numPoints <= 1)
return;
// 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)
{
if (paintingDisabled())
return;
syncPlatformContext(this);
platformContext()->drawHighlightForText(font, run, point, h, backgroundColor,
colorSpace, from, to, isActive);
}
} // namespace WebCore