diff -r 000000000000 -r 6474c204b198 gfx/2d/DrawTargetSkia.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/2d/DrawTargetSkia.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,839 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DrawTargetSkia.h" +#include "SourceSurfaceSkia.h" +#include "ScaledFontBase.h" +#include "ScaledFontCairo.h" +#include "skia/SkGpuDevice.h" +#include "skia/SkBitmapDevice.h" +#include "FilterNodeSoftware.h" + +#ifdef USE_SKIA_GPU +#include "skia/SkGpuDevice.h" +#include "skia/GrGLInterface.h" +#endif + +#include "skia/SkTypeface.h" +#include "skia/SkGradientShader.h" +#include "skia/SkBlurDrawLooper.h" +#include "skia/SkBlurMaskFilter.h" +#include "skia/SkColorFilter.h" +#include "skia/SkDropShadowImageFilter.h" +#include "skia/SkLayerRasterizer.h" +#include "skia/SkLayerDrawLooper.h" +#include "skia/SkDashPathEffect.h" +#include "Logging.h" +#include "HelpersSkia.h" +#include "Tools.h" +#include "DataSurfaceHelpers.h" +#include + +namespace mozilla { +namespace gfx { + +class GradientStopsSkia : public GradientStops +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia) + GradientStopsSkia(const std::vector& aStops, uint32_t aNumStops, ExtendMode aExtendMode) + : mCount(aNumStops) + , mExtendMode(aExtendMode) + { + if (mCount == 0) { + return; + } + + // Skia gradients always require a stop at 0.0 and 1.0, insert these if + // we don't have them. + uint32_t shift = 0; + if (aStops[0].offset != 0) { + mCount++; + shift = 1; + } + if (aStops[aNumStops-1].offset != 1) { + mCount++; + } + mColors.resize(mCount); + mPositions.resize(mCount); + if (aStops[0].offset != 0) { + mColors[0] = ColorToSkColor(aStops[0].color, 1.0); + mPositions[0] = 0; + } + for (uint32_t i = 0; i < aNumStops; i++) { + mColors[i + shift] = ColorToSkColor(aStops[i].color, 1.0); + mPositions[i + shift] = SkFloatToScalar(aStops[i].offset); + } + if (aStops[aNumStops-1].offset != 1) { + mColors[mCount-1] = ColorToSkColor(aStops[aNumStops-1].color, 1.0); + mPositions[mCount-1] = SK_Scalar1; + } + } + + BackendType GetBackendType() const { return BackendType::SKIA; } + + std::vector mColors; + std::vector mPositions; + int mCount; + ExtendMode mExtendMode; +}; + +/** + * When constructing a temporary SkBitmap via GetBitmapForSurface, we may also + * have to construct a temporary DataSourceSurface, which must live as long as + * the SkBitmap. So we return a pair of the SkBitmap and the (optional) + * temporary surface. + */ +struct TempBitmap +{ + SkBitmap mBitmap; + RefPtr mTmpSurface; +}; + +static TempBitmap +GetBitmapForSurface(SourceSurface* aSurface) +{ + TempBitmap result; + + if (aSurface->GetType() == SurfaceType::SKIA) { + result.mBitmap = static_cast(aSurface)->GetBitmap(); + return result; + } + + RefPtr surf = aSurface->GetDataSurface(); + if (!surf) { + MOZ_CRASH("Non-skia SourceSurfaces need to be DataSourceSurfaces"); + } + + result.mBitmap.setConfig(GfxFormatToSkiaConfig(surf->GetFormat()), + surf->GetSize().width, surf->GetSize().height, + surf->Stride()); + result.mBitmap.setPixels(surf->GetData()); + result.mTmpSurface = surf.forget(); + return result; +} + +DrawTargetSkia::DrawTargetSkia() + : mTexture(0), mSnapshot(nullptr) +{ +} + +DrawTargetSkia::~DrawTargetSkia() +{ +} + +TemporaryRef +DrawTargetSkia::Snapshot() +{ + RefPtr snapshot = mSnapshot; + if (!snapshot) { + snapshot = new SourceSurfaceSkia(); + mSnapshot = snapshot; + if (!snapshot->InitFromCanvas(mCanvas.get(), mFormat, this)) + return nullptr; + } + + return snapshot; +} + +static void +SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap, + Float aAlpha = 1.0) +{ + switch (aPattern.GetType()) { + case PatternType::COLOR: { + Color color = static_cast(aPattern).mColor; + aPaint.setColor(ColorToSkColor(color, aAlpha)); + break; + } + case PatternType::LINEAR_GRADIENT: { + const LinearGradientPattern& pat = static_cast(aPattern); + GradientStopsSkia *stops = static_cast(pat.mStops.get()); + SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode); + + if (stops->mCount >= 2) { + SkPoint points[2]; + points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y)); + points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y)); + + SkShader* shader = SkGradientShader::CreateLinear(points, + &stops->mColors.front(), + &stops->mPositions.front(), + stops->mCount, + mode); + + if (shader) { + SkMatrix mat; + GfxMatrixToSkiaMatrix(pat.mMatrix, mat); + shader->setLocalMatrix(mat); + SkSafeUnref(aPaint.setShader(shader)); + } + + } else { + aPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); + } + break; + } + case PatternType::RADIAL_GRADIENT: { + const RadialGradientPattern& pat = static_cast(aPattern); + GradientStopsSkia *stops = static_cast(pat.mStops.get()); + SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode); + + if (stops->mCount >= 2) { + SkPoint points[2]; + points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y)); + points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y)); + + SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0], + SkFloatToScalar(pat.mRadius1), + points[1], + SkFloatToScalar(pat.mRadius2), + &stops->mColors.front(), + &stops->mPositions.front(), + stops->mCount, + mode); + if (shader) { + SkMatrix mat; + GfxMatrixToSkiaMatrix(pat.mMatrix, mat); + shader->setLocalMatrix(mat); + SkSafeUnref(aPaint.setShader(shader)); + } + + } else { + aPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); + } + break; + } + case PatternType::SURFACE: { + const SurfacePattern& pat = static_cast(aPattern); + aTmpBitmap = GetBitmapForSurface(pat.mSurface); + const SkBitmap& bitmap = aTmpBitmap.mBitmap; + + SkShader::TileMode mode = ExtendModeToTileMode(pat.mExtendMode); + SkShader* shader = SkShader::CreateBitmapShader(bitmap, mode, mode); + SkMatrix mat; + GfxMatrixToSkiaMatrix(pat.mMatrix, mat); + shader->setLocalMatrix(mat); + SkSafeUnref(aPaint.setShader(shader)); + if (pat.mFilter == Filter::POINT) { + aPaint.setFilterLevel(SkPaint::kNone_FilterLevel); + } + break; + } + } +} + +static inline Rect +GetClipBounds(SkCanvas *aCanvas) +{ + SkRect clipBounds; + aCanvas->getClipBounds(&clipBounds); + return SkRectToRect(clipBounds); +} + +struct AutoPaintSetup { + AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern, const Rect* aMaskBounds = nullptr) + : mNeedsRestore(false), mAlpha(1.0) + { + Init(aCanvas, aOptions, aMaskBounds); + SetPaintPattern(mPaint, aPattern, mTmpBitmap, mAlpha); + } + + AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr) + : mNeedsRestore(false), mAlpha(1.0) + { + Init(aCanvas, aOptions, aMaskBounds); + } + + ~AutoPaintSetup() + { + if (mNeedsRestore) { + mCanvas->restore(); + } + } + + void Init(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds) + { + mPaint.setXfermodeMode(GfxOpToSkiaOp(aOptions.mCompositionOp)); + mCanvas = aCanvas; + + //TODO: Can we set greyscale somehow? + if (aOptions.mAntialiasMode != AntialiasMode::NONE) { + mPaint.setAntiAlias(true); + } else { + mPaint.setAntiAlias(false); + } + + Rect clipBounds = GetClipBounds(aCanvas); + bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) && + (!aMaskBounds || !aMaskBounds->Contains(clipBounds)); + + // TODO: We could skip the temporary for operator_source and just + // clear the clip rect. The other operators would be harder + // but could be worth it to skip pushing a group. + if (needsGroup) { + mPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); + SkPaint temp; + temp.setXfermodeMode(GfxOpToSkiaOp(aOptions.mCompositionOp)); + temp.setAlpha(U8CPU(aOptions.mAlpha*255)); + //TODO: Get a rect here + mCanvas->saveLayer(nullptr, &temp); + mNeedsRestore = true; + } else { + mPaint.setAlpha(U8CPU(aOptions.mAlpha*255.0)); + mAlpha = aOptions.mAlpha; + } + mPaint.setFilterLevel(SkPaint::kLow_FilterLevel); + } + + // TODO: Maybe add an operator overload to access this easier? + SkPaint mPaint; + TempBitmap mTmpBitmap; + bool mNeedsRestore; + SkCanvas* mCanvas; + Float mAlpha; +}; + +void +DrawTargetSkia::Flush() +{ + mCanvas->flush(); +} + +void +DrawTargetSkia::DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions, + const DrawOptions &aOptions) +{ + RefPtr dataSurface; + + if (!(aSurface->GetType() == SurfaceType::SKIA || aSurface->GetType() == SurfaceType::DATA)) { + dataSurface = aSurface->GetDataSurface(); + if (!dataSurface) { + gfxDebug() << *this << ": DrawSurface() can't draw surface"; + return; + } + aSurface = dataSurface.get(); + } + + if (aSource.IsEmpty()) { + return; + } + + MarkChanged(); + + SkRect destRect = RectToSkRect(aDest); + SkRect sourceRect = RectToSkRect(aSource); + + TempBitmap bitmap = GetBitmapForSurface(aSurface); + + AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest); + if (aSurfOptions.mFilter == Filter::POINT) { + paint.mPaint.setFilterLevel(SkPaint::kNone_FilterLevel); + } + + mCanvas->drawBitmapRectToRect(bitmap.mBitmap, &sourceRect, destRect, &paint.mPaint); +} + +void +DrawTargetSkia::DrawFilter(FilterNode *aNode, + const Rect &aSourceRect, + const Point &aDestPoint, + const DrawOptions &aOptions) +{ + FilterNodeSoftware* filter = static_cast(aNode); + filter->Draw(this, aSourceRect, aDestPoint, aOptions); +} + +void +DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator) +{ + if (!(aSurface->GetType() == SurfaceType::SKIA || aSurface->GetType() == SurfaceType::DATA)) { + return; + } + + MarkChanged(); + + mCanvas->save(SkCanvas::kMatrix_SaveFlag); + mCanvas->resetMatrix(); + + TempBitmap bitmap = GetBitmapForSurface(aSurface); + + SkPaint paint; + + SkImageFilter* filter = SkDropShadowImageFilter::Create(aOffset.x, aOffset.y, + aSigma, ColorToSkColor(aColor, 1.0)); + + paint.setImageFilter(filter); + paint.setXfermodeMode(GfxOpToSkiaOp(aOperator)); + + mCanvas->drawBitmap(bitmap.mBitmap, aDest.x, aDest.y, &paint); + mCanvas->restore(); +} + +void +DrawTargetSkia::FillRect(const Rect &aRect, + const Pattern &aPattern, + const DrawOptions &aOptions) +{ + MarkChanged(); + SkRect rect = RectToSkRect(aRect); + AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern, &aRect); + + mCanvas->drawRect(rect, paint.mPaint); +} + +void +DrawTargetSkia::Stroke(const Path *aPath, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + MarkChanged(); + MOZ_ASSERT(aPath, "Null path"); + if (aPath->GetBackendType() != BackendType::SKIA) { + return; + } + + const PathSkia *skiaPath = static_cast(aPath); + + + AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern); + if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) { + return; + } + + mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint); +} + +void +DrawTargetSkia::StrokeRect(const Rect &aRect, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + MarkChanged(); + AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern); + if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) { + return; + } + + mCanvas->drawRect(RectToSkRect(aRect), paint.mPaint); +} + +void +DrawTargetSkia::StrokeLine(const Point &aStart, + const Point &aEnd, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + MarkChanged(); + AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern); + if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) { + return; + } + + mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y), + SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y), + paint.mPaint); +} + +void +DrawTargetSkia::Fill(const Path *aPath, + const Pattern &aPattern, + const DrawOptions &aOptions) +{ + MarkChanged(); + if (aPath->GetBackendType() != BackendType::SKIA) { + return; + } + + const PathSkia *skiaPath = static_cast(aPath); + + AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern); + + mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint); +} + +void +DrawTargetSkia::FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions, + const GlyphRenderingOptions *aRenderingOptions) +{ + if (aFont->GetType() != FontType::MAC && + aFont->GetType() != FontType::SKIA && + aFont->GetType() != FontType::GDI) { + return; + } + + MarkChanged(); + + ScaledFontBase* skiaFont = static_cast(aFont); + + AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern); + paint.mPaint.setTypeface(skiaFont->GetSkTypeface()); + paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize)); + paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + if (aRenderingOptions && aRenderingOptions->GetType() == FontType::CAIRO) { + switch (static_cast(aRenderingOptions)->GetHinting()) { + case FontHinting::NONE: + paint.mPaint.setHinting(SkPaint::kNo_Hinting); + break; + case FontHinting::LIGHT: + paint.mPaint.setHinting(SkPaint::kSlight_Hinting); + break; + case FontHinting::NORMAL: + paint.mPaint.setHinting(SkPaint::kNormal_Hinting); + break; + case FontHinting::FULL: + paint.mPaint.setHinting(SkPaint::kFull_Hinting); + break; + } + + if (static_cast(aRenderingOptions)->GetAutoHinting()) { + paint.mPaint.setAutohinted(true); + } + } else { + paint.mPaint.setHinting(SkPaint::kNormal_Hinting); + } + + std::vector indices; + std::vector offsets; + indices.resize(aBuffer.mNumGlyphs); + offsets.resize(aBuffer.mNumGlyphs); + + for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { + indices[i] = aBuffer.mGlyphs[i].mIndex; + offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x); + offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y); + } + + mCanvas->drawPosText(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), paint.mPaint); +} + +void +DrawTargetSkia::Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions) +{ + MarkChanged(); + AutoPaintSetup paint(mCanvas.get(), aOptions, aSource); + + SkPaint maskPaint; + TempBitmap tmpBitmap; + SetPaintPattern(maskPaint, aMask, tmpBitmap); + + SkLayerRasterizer *raster = new SkLayerRasterizer(); + raster->addLayer(maskPaint); + SkSafeUnref(paint.mPaint.setRasterizer(raster)); + + mCanvas->drawRect(SkRectCoveringWholeSurface(), paint.mPaint); +} + +void +DrawTargetSkia::MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions) +{ + MarkChanged(); + AutoPaintSetup paint(mCanvas.get(), aOptions, aSource); + + SkPaint maskPaint; + TempBitmap tmpBitmap; + SetPaintPattern(maskPaint, SurfacePattern(aMask, ExtendMode::CLAMP), tmpBitmap); + + SkMatrix transform = maskPaint.getShader()->getLocalMatrix(); + transform.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y)); + maskPaint.getShader()->setLocalMatrix(transform); + + SkLayerRasterizer *raster = new SkLayerRasterizer(); + raster->addLayer(maskPaint); + SkSafeUnref(paint.mPaint.setRasterizer(raster)); + + IntSize size = aMask->GetSize(); + Rect rect = Rect(aOffset.x, aOffset.y, size.width, size.height); + mCanvas->drawRect(RectToSkRect(rect), paint.mPaint); +} + +TemporaryRef +DrawTargetSkia::CreateSourceSurfaceFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const +{ + RefPtr newSurf = new SourceSurfaceSkia(); + + if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) { + gfxDebug() << *this << ": Failure to create source surface from data. Size: " << aSize; + return nullptr; + } + + return newSurf; +} + +TemporaryRef +DrawTargetSkia::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const +{ + RefPtr target = new DrawTargetSkia(); + if (!target->Init(aSize, aFormat)) { + return nullptr; + } + return target; +} + +TemporaryRef +DrawTargetSkia::OptimizeSourceSurface(SourceSurface *aSurface) const +{ + if (aSurface->GetType() == SurfaceType::SKIA) { + return aSurface; + } + + return aSurface->GetDataSurface(); +} + +TemporaryRef +DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const +{ + return nullptr; +} + +void +DrawTargetSkia::CopySurface(SourceSurface *aSurface, + const IntRect& aSourceRect, + const IntPoint &aDestination) +{ + //TODO: We could just use writePixels() here if the sourceRect is the entire source + + if (aSurface->GetType() != SurfaceType::SKIA) { + return; + } + + MarkChanged(); + + TempBitmap bitmap = GetBitmapForSurface(aSurface); + + mCanvas->save(); + mCanvas->resetMatrix(); + SkRect dest = IntRectToSkRect(IntRect(aDestination.x, aDestination.y, aSourceRect.width, aSourceRect.height)); + SkIRect source = IntRectToSkIRect(aSourceRect); + mCanvas->clipRect(dest, SkRegion::kReplace_Op); + SkPaint paint; + + if (mCanvas->getDevice()->config() == SkBitmap::kRGB_565_Config) { + // Set the xfermode to SOURCE_OVER to workaround + // http://code.google.com/p/skia/issues/detail?id=628 + // RGB565 is opaque so they're equivalent anyway + paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); + } else { + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + } + + mCanvas->drawBitmapRect(bitmap.mBitmap, &source, dest, &paint); + mCanvas->restore(); +} + +bool +DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat) +{ + SkAutoTUnref device(new SkBitmapDevice(GfxFormatToSkiaConfig(aFormat), + aSize.width, aSize.height, + aFormat == SurfaceFormat::B8G8R8X8)); + + SkBitmap bitmap = device->accessBitmap(true); + if (!bitmap.allocPixels()) { + return false; + } + + bitmap.eraseARGB(0, 0, 0, 0); + + SkAutoTUnref canvas(new SkCanvas(device.get())); + mSize = aSize; + + mCanvas = canvas.get(); + mFormat = aFormat; + return true; +} + +#ifdef USE_SKIA_GPU +bool +DrawTargetSkia::InitWithGrContext(GrContext* aGrContext, + const IntSize &aSize, + SurfaceFormat aFormat) +{ + MOZ_ASSERT(aGrContext, "null GrContext"); + + mGrContext = aGrContext; + + mSize = aSize; + mFormat = aFormat; + + GrTextureDesc targetDescriptor; + + targetDescriptor.fFlags = kRenderTarget_GrTextureFlagBit; + targetDescriptor.fWidth = mSize.width; + targetDescriptor.fHeight = mSize.height; + targetDescriptor.fConfig = GfxFormatToGrConfig(mFormat); + targetDescriptor.fOrigin = kBottomLeft_GrSurfaceOrigin; + targetDescriptor.fSampleCnt = 0; + + SkAutoTUnref skiaTexture(mGrContext->createUncachedTexture(targetDescriptor, NULL, 0)); + if (!skiaTexture) { + return false; + } + + mTexture = (uint32_t)skiaTexture->getTextureHandle(); + + SkAutoTUnref device(new SkGpuDevice(mGrContext.get(), skiaTexture->asRenderTarget())); + SkAutoTUnref canvas(new SkCanvas(device.get())); + mCanvas = canvas.get(); + + return true; +} + +#endif + +void +DrawTargetSkia::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) +{ + SkAlphaType alphaType = kPremul_SkAlphaType; + if (aFormat == SurfaceFormat::B8G8R8X8) { + // We have to manually set the A channel to be 255 as Skia doesn't understand BGRX + ConvertBGRXToBGRA(aData, aSize, aStride); + alphaType = kOpaque_SkAlphaType; + } + + SkBitmap bitmap; + bitmap.setConfig(GfxFormatToSkiaConfig(aFormat), aSize.width, aSize.height, aStride, alphaType); + bitmap.setPixels(aData); + SkAutoTUnref canvas(new SkCanvas(new SkBitmapDevice(bitmap))); + + mSize = aSize; + mCanvas = canvas.get(); + mFormat = aFormat; +} + +void +DrawTargetSkia::SetTransform(const Matrix& aTransform) +{ + SkMatrix mat; + GfxMatrixToSkiaMatrix(aTransform, mat); + mCanvas->setMatrix(mat); + mTransform = aTransform; +} + +void* +DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType) +{ + if (aType == NativeSurfaceType::OPENGL_TEXTURE) { + return (void*)((uintptr_t)mTexture); + } + + return nullptr; +} + + +TemporaryRef +DrawTargetSkia::CreatePathBuilder(FillRule aFillRule) const +{ + RefPtr pb = new PathBuilderSkia(aFillRule); + return pb; +} + +void +DrawTargetSkia::ClearRect(const Rect &aRect) +{ + MarkChanged(); + SkPaint paint; + mCanvas->save(); + mCanvas->clipRect(RectToSkRect(aRect), SkRegion::kIntersect_Op, true); + paint.setColor(SkColorSetARGB(0, 0, 0, 0)); + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + mCanvas->drawPaint(paint); + mCanvas->restore(); +} + +void +DrawTargetSkia::PushClip(const Path *aPath) +{ + if (aPath->GetBackendType() != BackendType::SKIA) { + return; + } + + const PathSkia *skiaPath = static_cast(aPath); + mCanvas->save(SkCanvas::kClip_SaveFlag); + mCanvas->clipPath(skiaPath->GetPath(), SkRegion::kIntersect_Op, true); +} + +void +DrawTargetSkia::PushClipRect(const Rect& aRect) +{ + SkRect rect = RectToSkRect(aRect); + + mCanvas->save(SkCanvas::kClip_SaveFlag); + mCanvas->clipRect(rect, SkRegion::kIntersect_Op, true); +} + +void +DrawTargetSkia::PopClip() +{ + mCanvas->restore(); +} + +TemporaryRef +DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const +{ + std::vector stops; + stops.resize(aNumStops); + for (uint32_t i = 0; i < aNumStops; i++) { + stops[i] = aStops[i]; + } + std::stable_sort(stops.begin(), stops.end()); + + return new GradientStopsSkia(stops, aNumStops, aExtendMode); +} + +TemporaryRef +DrawTargetSkia::CreateFilter(FilterType aType) +{ + return FilterNodeSoftware::Create(aType); +} + +void +DrawTargetSkia::MarkChanged() +{ + if (mSnapshot) { + mSnapshot->DrawTargetWillChange(); + mSnapshot = nullptr; + } +} + +// Return a rect (in user space) that covers the entire surface by applying +// the inverse of GetTransform() to (0, 0, mSize.width, mSize.height). +SkRect +DrawTargetSkia::SkRectCoveringWholeSurface() const +{ + return RectToSkRect(mTransform.TransformBounds(Rect(0, 0, mSize.width, mSize.height))); +} + +void +DrawTargetSkia::SnapshotDestroyed() +{ + mSnapshot = nullptr; +} + +} +}