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