PlatformContextSkia.cpp [plain text]
#include "config.h"
#include "PlatformContextSkia.h"
#include "AffineTransform.h"
#include "DrawingBuffer.h"
#include "Extensions3D.h"
#include "GraphicsContext.h"
#include "GraphicsContext3D.h"
#include "ImageBuffer.h"
#include "NativeImageSkia.h"
#include "SkiaUtils.h"
#include "Texture.h"
#include "TilingData.h"
#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
#include "SkBitmap.h"
#include "SkColorPriv.h"
#include "SkDashPathEffect.h"
#include "SkShader.h"
#include "GrContext.h"
#include "SkGpuDevice.h"
#include "SkGpuDeviceFactory.h"
#include <wtf/MathExtras.h>
#include <wtf/Vector.h>
#if ENABLE(ACCELERATED_2D_CANVAS)
#include "GraphicsContextGPU.h"
#include "SharedGraphicsContext3D.h"
#endif
namespace WebCore {
extern bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path);
struct PlatformContextSkia::State {
State();
State(const State&);
~State();
float m_alpha;
SkXfermode::Mode m_xferMode;
bool m_useAntialiasing;
SkDrawLooper* m_looper;
SkColor m_fillColor;
SkShader* m_fillShader;
StrokeStyle m_strokeStyle;
SkColor m_strokeColor;
SkShader* m_strokeShader;
float m_strokeThickness;
int m_dashRatio; float m_miterLimit;
SkPaint::Cap m_lineCap;
SkPaint::Join m_lineJoin;
SkDashPathEffect* m_dash;
TextDrawingModeFlags m_textDrawingMode;
SkColor applyAlpha(SkColor) const;
SkBitmap m_imageBufferClip;
FloatRect m_clip;
WTF::Vector<SkPath> m_antiAliasClipPaths;
InterpolationQuality m_interpolationQuality;
bool m_canvasClipApplied;
PlatformContextSkia::State cloneInheritedProperties();
private:
void operator=(const State&);
};
PlatformContextSkia::State::State()
: m_alpha(1)
, m_xferMode(SkXfermode::kSrcOver_Mode)
, m_useAntialiasing(true)
, m_looper(0)
, m_fillColor(0xFF000000)
, m_fillShader(0)
, m_strokeStyle(SolidStroke)
, m_strokeColor(Color::black)
, m_strokeShader(0)
, m_strokeThickness(0)
, m_dashRatio(3)
, m_miterLimit(4)
, m_lineCap(SkPaint::kDefault_Cap)
, m_lineJoin(SkPaint::kDefault_Join)
, m_dash(0)
, m_textDrawingMode(TextModeFill)
, m_interpolationQuality(InterpolationHigh)
, m_canvasClipApplied(false)
{
}
PlatformContextSkia::State::State(const State& other)
: m_alpha(other.m_alpha)
, m_xferMode(other.m_xferMode)
, m_useAntialiasing(other.m_useAntialiasing)
, m_looper(other.m_looper)
, m_fillColor(other.m_fillColor)
, m_fillShader(other.m_fillShader)
, m_strokeStyle(other.m_strokeStyle)
, m_strokeColor(other.m_strokeColor)
, m_strokeShader(other.m_strokeShader)
, m_strokeThickness(other.m_strokeThickness)
, m_dashRatio(other.m_dashRatio)
, m_miterLimit(other.m_miterLimit)
, m_lineCap(other.m_lineCap)
, m_lineJoin(other.m_lineJoin)
, m_dash(other.m_dash)
, m_textDrawingMode(other.m_textDrawingMode)
, m_imageBufferClip(other.m_imageBufferClip)
, m_clip(other.m_clip)
, m_antiAliasClipPaths(other.m_antiAliasClipPaths)
, m_interpolationQuality(other.m_interpolationQuality)
, m_canvasClipApplied(other.m_canvasClipApplied)
{
SkSafeRef(m_looper);
SkSafeRef(m_dash);
SkSafeRef(m_fillShader);
SkSafeRef(m_strokeShader);
}
PlatformContextSkia::State::~State()
{
SkSafeUnref(m_looper);
SkSafeUnref(m_dash);
SkSafeUnref(m_fillShader);
SkSafeUnref(m_strokeShader);
}
PlatformContextSkia::State PlatformContextSkia::State::cloneInheritedProperties()
{
PlatformContextSkia::State state(*this);
state.m_antiAliasClipPaths.clear();
return state;
}
SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const
{
int s = roundf(m_alpha * 256);
if (s >= 256)
return c;
if (s < 0)
return 0;
int a = SkAlphaMul(SkColorGetA(c), s);
return (c & 0x00FFFFFF) | (a << 24);
}
PlatformContextSkia::PlatformContextSkia(SkCanvas* canvas)
: m_canvas(canvas)
, m_printing(false)
, m_drawingToImageBuffer(false)
, m_accelerationMode(NoAcceleration)
, m_backingStoreState(None)
{
m_stateStack.append(State());
m_state = &m_stateStack.last();
}
PlatformContextSkia::~PlatformContextSkia()
{
#if ENABLE(ACCELERATED_2D_CANVAS)
if (m_gpuCanvas) {
if (useSkiaGPU()) {
SharedGraphicsContext3D* context = m_gpuCanvas->context();
context->makeContextCurrent();
context->grContext()->flush(0);
}
m_gpuCanvas->drawingBuffer()->setWillPublishCallback(nullptr);
}
#endif
}
void PlatformContextSkia::setCanvas(SkCanvas* canvas)
{
m_canvas = canvas;
}
void PlatformContextSkia::setDrawingToImageBuffer(bool value)
{
m_drawingToImageBuffer = value;
}
bool PlatformContextSkia::isDrawingToImageBuffer() const
{
return m_drawingToImageBuffer;
}
void PlatformContextSkia::save()
{
ASSERT(!hasImageResamplingHint());
m_stateStack.append(m_state->cloneInheritedProperties());
m_state = &m_stateStack.last();
m_state->m_imageBufferClip.reset();
canvas()->save();
}
void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect,
const ImageBuffer* imageBuffer)
{
m_state->m_clip = rect;
SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()),
SkFloatToScalar(rect.maxX()), SkFloatToScalar(rect.maxY()) };
canvas()->clipRect(bounds);
canvas()->saveLayerAlpha(&bounds, 255,
static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap();
if (!bitmap->pixelRef()) {
bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config);
} else {
m_state->m_imageBufferClip = *bitmap;
}
}
void PlatformContextSkia::clipPathAntiAliased(const SkPath& clipPath)
{
bool haveLayerOutstanding = m_state->m_antiAliasClipPaths.size();
m_state->m_antiAliasClipPaths.append(clipPath);
if (!haveLayerOutstanding) {
SkRect bounds = clipPath.getBounds();
canvas()->saveLayerAlpha(&bounds, 255, static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag));
canvas()->save();
}
}
void PlatformContextSkia::restore()
{
if (!m_state->m_imageBufferClip.empty()) {
applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip);
canvas()->restore();
}
if (!m_state->m_antiAliasClipPaths.isEmpty())
applyAntiAliasedClipPaths(m_state->m_antiAliasClipPaths);
m_stateStack.removeLast();
m_state = &m_stateStack.last();
canvas()->restore();
}
void PlatformContextSkia::drawRect(SkRect rect)
{
SkPaint paint;
int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000;
if (fillcolorNotTransparent) {
setupPaintForFilling(&paint);
canvas()->drawRect(rect, paint);
}
if (m_state->m_strokeStyle != NoStroke
&& (m_state->m_strokeColor & 0xFF000000)) {
SkColor oldFillColor = m_state->m_fillColor;
SkShader* oldFillShader = m_state->m_fillShader;
SkSafeRef(oldFillShader);
setFillColor(m_state->m_strokeColor);
paint.reset();
setupPaintForFilling(&paint);
SkRect topBorder = { rect.fLeft, rect.fTop, rect.fRight, rect.fTop + 1 };
canvas()->drawRect(topBorder, paint);
SkRect bottomBorder = { rect.fLeft, rect.fBottom - 1, rect.fRight, rect.fBottom };
canvas()->drawRect(bottomBorder, paint);
SkRect leftBorder = { rect.fLeft, rect.fTop + 1, rect.fLeft + 1, rect.fBottom - 1 };
canvas()->drawRect(leftBorder, paint);
SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, rect.fRight, rect.fBottom - 1 };
canvas()->drawRect(rightBorder, paint);
setFillColor(oldFillColor);
setFillShader(oldFillShader);
SkSafeUnref(oldFillShader);
}
}
void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const
{
#if defined(SK_DEBUG)
{
SkPaint defaultPaint;
SkASSERT(*paint == defaultPaint);
}
#endif
paint->setAntiAlias(m_state->m_useAntialiasing);
paint->setXfermodeMode(m_state->m_xferMode);
paint->setLooper(m_state->m_looper);
}
void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const
{
setupPaintCommon(paint);
paint->setColor(m_state->applyAlpha(m_state->m_fillColor));
paint->setShader(m_state->m_fillShader);
}
float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const
{
setupPaintCommon(paint);
float width = m_state->m_strokeThickness;
paint->setColor(m_state->applyAlpha(m_state->m_strokeColor));
paint->setShader(m_state->m_strokeShader);
paint->setStyle(SkPaint::kStroke_Style);
paint->setStrokeWidth(SkFloatToScalar(width));
paint->setStrokeCap(m_state->m_lineCap);
paint->setStrokeJoin(m_state->m_lineJoin);
paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit));
if (m_state->m_dash)
paint->setPathEffect(m_state->m_dash);
else {
switch (m_state->m_strokeStyle) {
case NoStroke:
case SolidStroke:
break;
case DashedStroke:
width = m_state->m_dashRatio * width;
case DottedStroke:
int dashLength = static_cast<int>(width);
int distance = length - 2 * static_cast<int>(m_state->m_strokeThickness);
int phase = 1;
if (dashLength > 1) {
int numDashes = distance / dashLength;
int remainder = distance % dashLength;
if (numDashes % 2 == 0) {
phase = (dashLength - remainder) / 2;
} else {
phase = dashLength - remainder / 2;
}
}
SkScalar dashLengthSk = SkIntToScalar(dashLength);
SkScalar intervals[2] = { dashLengthSk, dashLengthSk };
paint->setPathEffect(new SkDashPathEffect(intervals, 2, SkIntToScalar(phase)))->unref();
}
}
return width;
}
void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl)
{
SkRefCnt_SafeAssign(m_state->m_looper, dl);
}
void PlatformContextSkia::setMiterLimit(float ml)
{
m_state->m_miterLimit = ml;
}
void PlatformContextSkia::setAlpha(float alpha)
{
m_state->m_alpha = alpha;
}
void PlatformContextSkia::setLineCap(SkPaint::Cap lc)
{
m_state->m_lineCap = lc;
}
void PlatformContextSkia::setLineJoin(SkPaint::Join lj)
{
m_state->m_lineJoin = lj;
}
void PlatformContextSkia::setXfermodeMode(SkXfermode::Mode pdm)
{
m_state->m_xferMode = pdm;
}
void PlatformContextSkia::setFillColor(SkColor color)
{
m_state->m_fillColor = color;
setFillShader(0);
}
SkDrawLooper* PlatformContextSkia::getDrawLooper() const
{
return m_state->m_looper;
}
StrokeStyle PlatformContextSkia::getStrokeStyle() const
{
return m_state->m_strokeStyle;
}
void PlatformContextSkia::setStrokeStyle(StrokeStyle strokeStyle)
{
m_state->m_strokeStyle = strokeStyle;
}
void PlatformContextSkia::setStrokeColor(SkColor strokeColor)
{
m_state->m_strokeColor = strokeColor;
setStrokeShader(0);
}
float PlatformContextSkia::getStrokeThickness() const
{
return m_state->m_strokeThickness;
}
void PlatformContextSkia::setStrokeThickness(float thickness)
{
m_state->m_strokeThickness = thickness;
}
void PlatformContextSkia::setStrokeShader(SkShader* strokeShader)
{
if (strokeShader)
m_state->m_strokeColor = Color::black;
if (strokeShader != m_state->m_strokeShader) {
SkSafeUnref(m_state->m_strokeShader);
m_state->m_strokeShader = strokeShader;
SkSafeRef(m_state->m_strokeShader);
}
}
TextDrawingModeFlags PlatformContextSkia::getTextDrawingMode() const
{
return m_state->m_textDrawingMode;
}
float PlatformContextSkia::getAlpha() const
{
return m_state->m_alpha;
}
int PlatformContextSkia::getNormalizedAlpha() const
{
int alpha = roundf(m_state->m_alpha * 256);
if (alpha > 255)
alpha = 255;
else if (alpha < 0)
alpha = 0;
return alpha;
}
void PlatformContextSkia::setTextDrawingMode(TextDrawingModeFlags mode)
{
ASSERT(!(mode & TextModeClip));
m_state->m_textDrawingMode = mode;
}
void PlatformContextSkia::setUseAntialiasing(bool enable)
{
m_state->m_useAntialiasing = enable;
}
SkColor PlatformContextSkia::effectiveFillColor() const
{
return m_state->applyAlpha(m_state->m_fillColor);
}
SkColor PlatformContextSkia::effectiveStrokeColor() const
{
return m_state->applyAlpha(m_state->m_strokeColor);
}
void PlatformContextSkia::canvasClipPath(const SkPath& path)
{
m_state->m_canvasClipApplied = true;
m_canvas->clipPath(path);
}
void PlatformContextSkia::setFillShader(SkShader* fillShader)
{
if (fillShader)
m_state->m_fillColor = Color::black;
if (fillShader != m_state->m_fillShader) {
SkSafeUnref(m_state->m_fillShader);
m_state->m_fillShader = fillShader;
SkSafeRef(m_state->m_fillShader);
}
}
InterpolationQuality PlatformContextSkia::interpolationQuality() const
{
return m_state->m_interpolationQuality;
}
void PlatformContextSkia::setInterpolationQuality(InterpolationQuality interpolationQuality)
{
m_state->m_interpolationQuality = interpolationQuality;
}
void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash)
{
if (dash != m_state->m_dash) {
SkSafeUnref(m_state->m_dash);
m_state->m_dash = dash;
}
}
void PlatformContextSkia::paintSkPaint(const SkRect& rect,
const SkPaint& paint)
{
m_canvas->drawRect(rect, paint);
}
const SkBitmap* PlatformContextSkia::bitmap() const
{
return &m_canvas->getDevice()->accessBitmap(false);
}
bool PlatformContextSkia::isNativeFontRenderingAllowed()
{
#if ENABLE(SKIA_TEXT)
return false;
#else
if (m_accelerationMode == SkiaGPU)
return false;
return skia::SupportsPlatformPaint(m_canvas);
#endif
}
void PlatformContextSkia::getImageResamplingHint(IntSize* srcSize, FloatSize* dstSize) const
{
*srcSize = m_imageResamplingHintSrcSize;
*dstSize = m_imageResamplingHintDstSize;
}
void PlatformContextSkia::setImageResamplingHint(const IntSize& srcSize, const FloatSize& dstSize)
{
m_imageResamplingHintSrcSize = srcSize;
m_imageResamplingHintDstSize = dstSize;
}
void PlatformContextSkia::clearImageResamplingHint()
{
m_imageResamplingHintSrcSize = IntSize();
m_imageResamplingHintDstSize = FloatSize();
}
bool PlatformContextSkia::hasImageResamplingHint() const
{
return !m_imageResamplingHintSrcSize.isEmpty() && !m_imageResamplingHintDstSize.isEmpty();
}
void PlatformContextSkia::applyClipFromImage(const FloatRect& rect, const SkBitmap& imageBuffer)
{
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint);
}
void PlatformContextSkia::applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths)
{
m_canvas->restore();
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kClear_Mode);
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kFill_Style);
for (size_t i = paths.size() - 1; i < paths.size(); --i) {
paths[i].toggleInverseFillType();
m_canvas->drawPath(paths[i], paint);
}
m_canvas->restore();
}
bool PlatformContextSkia::canAccelerate() const
{
return !m_state->m_fillShader; }
bool PlatformContextSkia::canvasClipApplied() const
{
return m_state->m_canvasClipApplied;
}
class WillPublishCallbackImpl : public DrawingBuffer::WillPublishCallback {
public:
static PassOwnPtr<WillPublishCallback> create(PlatformContextSkia* pcs)
{
return adoptPtr(new WillPublishCallbackImpl(pcs));
}
virtual void willPublish()
{
m_pcs->prepareForHardwareDraw();
}
private:
explicit WillPublishCallbackImpl(PlatformContextSkia* pcs)
: m_pcs(pcs)
{
}
PlatformContextSkia* m_pcs;
};
void PlatformContextSkia::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* drawingBuffer, const WebCore::IntSize& size)
{
m_accelerationMode = NoAcceleration;
#if ENABLE(ACCELERATED_2D_CANVAS)
if (context && drawingBuffer) {
m_gpuCanvas = adoptPtr(new GraphicsContextGPU(context, drawingBuffer, size));
m_uploadTexture.clear();
drawingBuffer->setWillPublishCallback(WillPublishCallbackImpl::create(this));
GrContext* gr = context->grContext();
if (gr) {
m_accelerationMode = SkiaGPU;
context->makeContextCurrent();
m_gpuCanvas->bindFramebuffer();
gr->resetContext();
drawingBuffer->setGrContext(gr);
GrPlatformSurfaceDesc drawBufDesc;
drawingBuffer->getGrPlatformSurfaceDesc(&drawBufDesc);
GrTexture* drawBufTex = static_cast<GrTexture*>(gr->createPlatformSurface(drawBufDesc));
SkDeviceFactory* factory = new SkGpuDeviceFactory(gr, drawBufTex);
drawBufTex->unref();
SkDevice* device = factory->newDevice(m_canvas, SkBitmap::kARGB_8888_Config, drawingBuffer->size().width(), drawingBuffer->size().height(), false, false);
m_canvas->setDevice(device)->unref();
m_canvas->setDeviceFactory(factory);
} else
m_accelerationMode = GPU;
} else {
syncSoftwareCanvas();
m_uploadTexture.clear();
m_gpuCanvas.clear();
}
#endif
}
void PlatformContextSkia::prepareForSoftwareDraw() const
{
if (m_accelerationMode == SkiaGPU) {
#if ENABLE(ACCELERATED_2D_CANVAS)
if (m_gpuCanvas)
m_gpuCanvas->context()->makeContextCurrent();
#endif
return;
}
if (m_backingStoreState == Hardware) {
if (m_state->m_xferMode == SkXfermode::kSrcOver_Mode) {
m_backingStoreState = Mixed;
} else {
readbackHardwareToSoftware();
m_backingStoreState = Software;
}
} else if (m_backingStoreState == Mixed) {
if (m_state->m_xferMode != SkXfermode::kSrcOver_Mode) {
uploadSoftwareToHardware(CompositeSourceOver);
readbackHardwareToSoftware();
m_backingStoreState = Software;
}
} else if (m_backingStoreState == None) {
m_backingStoreState = Software;
}
}
void PlatformContextSkia::prepareForHardwareDraw() const
{
if (!(m_accelerationMode == GPU))
return;
if (m_backingStoreState == Software) {
uploadSoftwareToHardware(CompositeCopy);
} else if (m_backingStoreState == Mixed) {
uploadSoftwareToHardware(CompositeSourceOver);
}
m_backingStoreState = Hardware;
}
void PlatformContextSkia::syncSoftwareCanvas() const
{
if (m_accelerationMode == SkiaGPU) {
#if ENABLE(ACCELERATED_2D_CANVAS)
if (m_gpuCanvas)
m_gpuCanvas->context()->makeContextCurrent();
#endif
return;
}
if (m_backingStoreState == Hardware)
readbackHardwareToSoftware();
else if (m_backingStoreState == Mixed) {
uploadSoftwareToHardware(CompositeSourceOver);
readbackHardwareToSoftware();
m_backingStoreState = Software;
}
m_backingStoreState = Software;
}
void PlatformContextSkia::markDirtyRect(const IntRect& rect)
{
if (m_accelerationMode != GPU)
return;
switch (m_backingStoreState) {
case Software:
case Mixed:
m_softwareDirtyRect.unite(rect);
return;
case Hardware:
return;
default:
ASSERT_NOT_REACHED();
}
}
void PlatformContextSkia::uploadSoftwareToHardware(CompositeOperator op) const
{
#if ENABLE(ACCELERATED_2D_CANVAS)
const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(false);
SkAutoLockPixels lock(bitmap);
SharedGraphicsContext3D* context = m_gpuCanvas->context();
if (!m_uploadTexture || m_uploadTexture->tiles().totalSizeX() < bitmap.width() || m_uploadTexture->tiles().totalSizeY() < bitmap.height())
m_uploadTexture = context->createTexture(Texture::BGRA8, bitmap.width(), bitmap.height());
m_uploadTexture->updateSubRect(bitmap.getPixels(), m_softwareDirtyRect);
AffineTransform identity;
gpuCanvas()->drawTexturedRect(m_uploadTexture.get(), m_softwareDirtyRect, m_softwareDirtyRect, identity, 1.0, ColorSpaceDeviceRGB, op, false);
m_canvas->save();
m_canvas->resetMatrix();
SkRect bounds = m_softwareDirtyRect;
m_canvas->clipRect(bounds, SkRegion::kReplace_Op);
m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
m_canvas->restore();
m_softwareDirtyRect.setWidth(0); #endif
}
void PlatformContextSkia::readbackHardwareToSoftware() const
{
#if ENABLE(ACCELERATED_2D_CANVAS)
const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(true);
SkAutoLockPixels lock(bitmap);
int width = bitmap.width(), height = bitmap.height();
SharedGraphicsContext3D* context = m_gpuCanvas->context();
m_gpuCanvas->bindFramebuffer();
for (int y = 0; y < height; ++y) {
uint32_t* pixels = bitmap.getAddr32(0, y);
if (context->supportsBGRA())
context->readPixels(0, height - 1 - y, width, 1, Extensions3D::BGRA_EXT, GraphicsContext3D::UNSIGNED_BYTE, pixels);
else {
context->readPixels(0, height - 1 - y, width, 1, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixels);
for (int i = 0; i < width; ++i) {
uint32_t pixel = pixels[i];
pixels[i] = (pixel & 0xFF00FF00) | ((pixel & 0x00FF0000) >> 16) | ((pixel & 0x000000FF) << 16);
}
}
}
m_softwareDirtyRect.unite(IntRect(0, 0, width, height)); #endif
}
}