12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150 |
- /* -*- 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/include/core/SkBitmapDevice.h"
- #include "FilterNodeSoftware.h"
- #include "HelpersSkia.h"
- #include "mozilla/ArrayUtils.h"
- #include "skia/include/core/SkSurface.h"
- #include "skia/include/core/SkTypeface.h"
- #include "skia/include/effects/SkGradientShader.h"
- #include "skia/include/core/SkColorFilter.h"
- #include "skia/include/effects/SkBlurImageFilter.h"
- #include "skia/include/effects/SkLayerRasterizer.h"
- #include "skia/src/core/SkSpecialImage.h"
- #include "Blur.h"
- #include "Logging.h"
- #include "Tools.h"
- #include "DataSurfaceHelpers.h"
- #include <algorithm>
- #ifdef USE_SKIA_GPU
- #include "GLDefs.h"
- #include "skia/include/gpu/SkGr.h"
- #include "skia/include/gpu/GrContext.h"
- #include "skia/include/gpu/GrDrawContext.h"
- #include "skia/include/gpu/gl/GrGLInterface.h"
- #include "skia/src/image/SkImage_Gpu.h"
- #endif
- #ifdef MOZ_WIDGET_COCOA
- #include "BorrowedContext.h"
- #include <ApplicationServices/ApplicationServices.h>
- #include "mozilla/Vector.h"
- #include "ScaledFontMac.h"
- #include "CGTextDrawing.h"
- #endif
- #ifdef XP_WIN
- #include "ScaledFontDWrite.h"
- #endif
- namespace mozilla {
- namespace gfx {
- class GradientStopsSkia : public GradientStops
- {
- public:
- MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia)
- GradientStopsSkia(const std::vector<GradientStop>& 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<SkColor> mColors;
- std::vector<SkScalar> mPositions;
- int mCount;
- ExtendMode mExtendMode;
- };
- /**
- * When constructing a temporary SkImage via GetSkImageForSurface, we may also
- * have to construct a temporary DataSourceSurface, which must live as long as
- * the SkImage. We attach this temporary surface to the image's pixelref, so
- * that it can be released once the pixelref is freed.
- */
- static void
- ReleaseTemporarySurface(const void* aPixels, void* aContext)
- {
- DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
- if (surf) {
- surf->Release();
- }
- }
- #ifdef IS_BIG_ENDIAN
- static const int kARGBAlphaOffset = 0;
- #else
- static const int kARGBAlphaOffset = 3;
- #endif
- static void
- WriteRGBXFormat(uint8_t* aData, const IntSize &aSize,
- const int32_t aStride, SurfaceFormat aFormat)
- {
- if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
- return;
- }
- int height = aSize.height;
- int width = aSize.width * 4;
- for (int row = 0; row < height; ++row) {
- for (int column = 0; column < width; column += 4) {
- aData[column + kARGBAlphaOffset] = 0xFF;
- }
- aData += aStride;
- }
- return;
- }
- #ifdef DEBUG
- static bool
- VerifyRGBXFormat(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
- {
- if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
- return true;
- }
- // We should've initialized the data to be opaque already
- // On debug builds, verify that this is actually true.
- int height = aSize.height;
- int width = aSize.width * 4;
- for (int row = 0; row < height; ++row) {
- for (int column = 0; column < width; column += 4) {
- if (aData[column + kARGBAlphaOffset] != 0xFF) {
- gfxCriticalError() << "RGBX pixel at (" << column << "," << row << ") in "
- << width << "x" << height << " surface is not opaque: "
- << int(aData[column]) << ","
- << int(aData[column+1]) << ","
- << int(aData[column+2]) << ","
- << int(aData[column+3]);
- }
- }
- aData += aStride;
- }
- return true;
- }
- // Since checking every pixel is expensive, this only checks the four corners and center
- // of a surface that their alpha value is 0xFF.
- static bool
- VerifyRGBXCorners(uint8_t* aData, const IntSize &aSize, const int32_t aStride, SurfaceFormat aFormat)
- {
- if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
- return true;
- }
- int height = aSize.height;
- int width = aSize.width;
- const int pixelSize = 4;
- const int strideDiff = aStride - (width * pixelSize);
- MOZ_ASSERT(width * pixelSize <= aStride);
- const int topLeft = 0;
- const int topRight = width * pixelSize - pixelSize;
- const int bottomRight = aStride * height - strideDiff - pixelSize;
- const int bottomLeft = aStride * height - aStride;
- // Lastly the center pixel
- int middleRowHeight = height / 2;
- int middleRowWidth = (width / 2) * pixelSize;
- const int middle = aStride * middleRowHeight + middleRowWidth;
- const int offsets[] = { topLeft, topRight, bottomRight, bottomLeft, middle };
- for (size_t i = 0; i < MOZ_ARRAY_LENGTH(offsets); i++) {
- int offset = offsets[i];
- if (aData[offset + kARGBAlphaOffset] != 0xFF) {
- int row = offset / aStride;
- int column = (offset % aStride) / pixelSize;
- gfxCriticalError() << "RGBX corner pixel at (" << column << "," << row << ") in "
- << width << "x" << height << " surface is not opaque: "
- << int(aData[offset]) << ","
- << int(aData[offset+1]) << ","
- << int(aData[offset+2]) << ","
- << int(aData[offset+3]);
- }
- }
- return true;
- }
- #endif
- static sk_sp<SkImage>
- GetSkImageForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>* aLock)
- {
- if (!aSurface) {
- gfxDebug() << "Creating null Skia image from null SourceSurface";
- return nullptr;
- }
- if (aSurface->GetType() == SurfaceType::SKIA) {
- return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
- }
- DataSourceSurface* surf = aSurface->GetDataSurface().take();
- if (!surf) {
- gfxWarning() << "Failed getting DataSourceSurface for Skia image";
- return nullptr;
- }
- SkPixmap pixmap(MakeSkiaImageInfo(surf->GetSize(), surf->GetFormat()),
- surf->GetData(), surf->Stride());
- sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, ReleaseTemporarySurface, surf);
- if (!image) {
- ReleaseTemporarySurface(nullptr, surf);
- gfxDebug() << "Failed making Skia raster image for temporary surface";
- }
- // Skia doesn't support RGBX surfaces so ensure that the alpha value is opaque white.
- MOZ_ASSERT(VerifyRGBXCorners(surf->GetData(), surf->GetSize(),
- surf->Stride(), surf->GetFormat()));
- return image;
- }
- DrawTargetSkia::DrawTargetSkia()
- : mSnapshot(nullptr)
- #ifdef MOZ_WIDGET_COCOA
- , mCG(nullptr)
- , mColorSpace(nullptr)
- , mCanvasData(nullptr)
- , mCGSize(0, 0)
- #endif
- {
- }
- DrawTargetSkia::~DrawTargetSkia()
- {
- #ifdef MOZ_WIDGET_COCOA
- if (mCG) {
- CGContextRelease(mCG);
- mCG = nullptr;
- }
- if (mColorSpace) {
- CGColorSpaceRelease(mColorSpace);
- mColorSpace = nullptr;
- }
- #endif
- }
- already_AddRefed<SourceSurface>
- DrawTargetSkia::Snapshot()
- {
- RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
- if (mSurface && !snapshot) {
- snapshot = new SourceSurfaceSkia();
- sk_sp<SkImage> image;
- // If the surface is raster, making a snapshot may trigger a pixel copy.
- // Instead, try to directly make a raster image referencing the surface pixels.
- SkPixmap pixmap;
- if (mSurface->peekPixels(&pixmap)) {
- image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
- } else {
- image = mSurface->makeImageSnapshot(SkBudgeted::kNo);
- }
- if (!snapshot->InitFromImage(image, mFormat, this)) {
- return nullptr;
- }
- mSnapshot = snapshot;
- }
- return snapshot.forget();
- }
- bool
- DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize,
- int32_t* aStride, SurfaceFormat* aFormat,
- IntPoint* aOrigin)
- {
- // Ensure the layer is at the origin if required.
- SkIPoint origin = mCanvas->getTopDevice()->getOrigin();
- if (!aOrigin && !origin.isZero()) {
- return false;
- }
- /* Test if the canvas' device has accessible pixels first, as actually
- * accessing the pixels may trigger side-effects, even if it fails.
- */
- if (!mCanvas->peekPixels(nullptr)) {
- return false;
- }
- SkImageInfo info;
- size_t rowBytes;
- void* pixels = mCanvas->accessTopLayerPixels(&info, &rowBytes);
- if (!pixels) {
- return false;
- }
- MarkChanged();
- *aData = reinterpret_cast<uint8_t*>(pixels);
- *aSize = IntSize(info.width(), info.height());
- *aStride = int32_t(rowBytes);
- *aFormat = SkiaColorTypeToGfxFormat(info.colorType(), info.alphaType());
- if (aOrigin) {
- *aOrigin = IntPoint(origin.x(), origin.y());
- }
- return true;
- }
- void
- DrawTargetSkia::ReleaseBits(uint8_t* aData)
- {
- }
- static void
- ReleaseImage(const void* aPixels, void* aContext)
- {
- SkImage* image = static_cast<SkImage*>(aContext);
- SkSafeUnref(image);
- }
- static sk_sp<SkImage>
- ExtractSubset(sk_sp<SkImage> aImage, const IntRect& aRect)
- {
- SkIRect subsetRect = IntRectToSkIRect(aRect);
- if (aImage->bounds() == subsetRect) {
- return aImage;
- }
- // makeSubset is slow, so prefer to use SkPixmap::extractSubset where possible.
- SkPixmap pixmap, subsetPixmap;
- if (aImage->peekPixels(&pixmap) &&
- pixmap.extractSubset(&subsetPixmap, subsetRect)) {
- // Release the original image reference so only the subset image keeps it alive.
- return SkImage::MakeFromRaster(subsetPixmap, ReleaseImage, aImage.release());
- }
- return aImage->makeSubset(subsetRect);
- }
- static inline bool
- SkImageIsMask(const sk_sp<SkImage>& aImage)
- {
- SkPixmap pixmap;
- if (aImage->peekPixels(&pixmap)) {
- return pixmap.colorType() == kAlpha_8_SkColorType;
- #ifdef USE_SKIA_GPU
- } else if (GrTexture* tex = aImage->getTexture()) {
- return GrPixelConfigIsAlphaOnly(tex->config());
- #endif
- } else {
- return false;
- }
- }
- static bool
- ExtractAlphaBitmap(sk_sp<SkImage> aImage, SkBitmap* aResultBitmap)
- {
- SkImageInfo info = SkImageInfo::MakeA8(aImage->width(), aImage->height());
- SkBitmap bitmap;
- if (!bitmap.tryAllocPixels(info, SkAlign4(info.minRowBytes())) ||
- !aImage->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0)) {
- gfxWarning() << "Failed reading alpha pixels for Skia bitmap";
- return false;
- }
- *aResultBitmap = bitmap;
- return true;
- }
- static sk_sp<SkImage>
- ExtractAlphaForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>& aLock)
- {
- sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &aLock);
- if (!image) {
- return nullptr;
- }
- if (SkImageIsMask(image)) {
- return image;
- }
- SkBitmap bitmap;
- if (!ExtractAlphaBitmap(image, &bitmap)) {
- return nullptr;
- }
- // Mark the bitmap immutable so that it will be shared rather than copied.
- bitmap.setImmutable();
- return SkImage::MakeFromBitmap(bitmap);
- }
- static void
- SetPaintPattern(SkPaint& aPaint,
- const Pattern& aPattern,
- Maybe<MutexAutoLock>& aLock,
- Float aAlpha = 1.0,
- Point aOffset = Point(0, 0))
- {
- switch (aPattern.GetType()) {
- case PatternType::COLOR: {
- Color color = static_cast<const ColorPattern&>(aPattern).mColor;
- aPaint.setColor(ColorToSkColor(color, aAlpha));
- break;
- }
- case PatternType::LINEAR_GRADIENT: {
- const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
- GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
- if (!stops || stops->mCount < 2 ||
- !pat.mBegin.IsFinite() || !pat.mEnd.IsFinite()) {
- aPaint.setColor(SK_ColorTRANSPARENT);
- } else {
- SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
- 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));
- SkMatrix mat;
- GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
- mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
- sk_sp<SkShader> shader = SkGradientShader::MakeLinear(points,
- &stops->mColors.front(),
- &stops->mPositions.front(),
- stops->mCount,
- mode, 0, &mat);
- aPaint.setShader(shader);
- }
- break;
- }
- case PatternType::RADIAL_GRADIENT: {
- const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
- GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
- if (!stops || stops->mCount < 2 ||
- !pat.mCenter1.IsFinite() || !IsFinite(pat.mRadius1) ||
- !pat.mCenter2.IsFinite() || !IsFinite(pat.mRadius2)) {
- aPaint.setColor(SK_ColorTRANSPARENT);
- } else {
- SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
- 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));
- SkMatrix mat;
- GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
- mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
- sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(points[0],
- SkFloatToScalar(pat.mRadius1),
- points[1],
- SkFloatToScalar(pat.mRadius2),
- &stops->mColors.front(),
- &stops->mPositions.front(),
- stops->mCount,
- mode, 0, &mat);
- aPaint.setShader(shader);
- }
- break;
- }
- case PatternType::SURFACE: {
- const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
- sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface, &aLock);
- if (!image) {
- aPaint.setColor(SK_ColorTRANSPARENT);
- break;
- }
- SkMatrix mat;
- GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
- mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
- if (!pat.mSamplingRect.IsEmpty()) {
- image = ExtractSubset(image, pat.mSamplingRect);
- mat.preTranslate(pat.mSamplingRect.x, pat.mSamplingRect.y);
- }
- SkShader::TileMode xTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS);
- SkShader::TileMode yTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS);
- aPaint.setShader(image->makeShader(xTileMode, yTileMode, &mat));
- if (pat.mSamplingFilter == SamplingFilter::POINT) {
- aPaint.setFilterQuality(kNone_SkFilterQuality);
- }
- break;
- }
- }
- }
- static inline Rect
- GetClipBounds(SkCanvas *aCanvas)
- {
- // Use a manually transformed getClipDeviceBounds instead of
- // getClipBounds because getClipBounds inflates the the bounds
- // by a pixel in each direction to compensate for antialiasing.
- SkIRect deviceBounds;
- if (!aCanvas->getClipDeviceBounds(&deviceBounds)) {
- return Rect();
- }
- SkMatrix inverseCTM;
- if (!aCanvas->getTotalMatrix().invert(&inverseCTM)) {
- return Rect();
- }
- SkRect localBounds;
- inverseCTM.mapRect(&localBounds, SkRect::Make(deviceBounds));
- return SkRectToRect(localBounds);
- }
- struct AutoPaintSetup {
- AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern, const Rect* aMaskBounds = nullptr, Point aOffset = Point(0, 0))
- : mNeedsRestore(false), mAlpha(1.0)
- {
- Init(aCanvas, aOptions, aMaskBounds, false);
- SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aOffset);
- }
- AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
- : mNeedsRestore(false), mAlpha(1.0)
- {
- Init(aCanvas, aOptions, aMaskBounds, aForceGroup);
- }
- ~AutoPaintSetup()
- {
- if (mNeedsRestore) {
- mCanvas->restore();
- }
- }
- void Init(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds, bool aForceGroup)
- {
- mPaint.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
- mCanvas = aCanvas;
- //TODO: Can we set greyscale somehow?
- if (aOptions.mAntialiasMode != AntialiasMode::NONE) {
- mPaint.setAntiAlias(true);
- } else {
- mPaint.setAntiAlias(false);
- }
- bool needsGroup = aForceGroup ||
- (!IsOperatorBoundByMask(aOptions.mCompositionOp) &&
- (!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas))));
- // 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.setBlendMode(SkBlendMode::kSrcOver);
- SkPaint temp;
- temp.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
- temp.setAlpha(ColorFloatToByte(aOptions.mAlpha));
- //TODO: Get a rect here
- mCanvas->saveLayer(nullptr, &temp);
- mNeedsRestore = true;
- } else {
- mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha));
- mAlpha = aOptions.mAlpha;
- }
- mPaint.setFilterQuality(kLow_SkFilterQuality);
- }
- // TODO: Maybe add an operator overload to access this easier?
- SkPaint mPaint;
- bool mNeedsRestore;
- SkCanvas* mCanvas;
- Maybe<MutexAutoLock> mLock;
- Float mAlpha;
- };
- void
- DrawTargetSkia::Flush()
- {
- mCanvas->flush();
- }
- void
- DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
- const Rect &aDest,
- const Rect &aSource,
- const DrawSurfaceOptions &aSurfOptions,
- const DrawOptions &aOptions)
- {
- if (aSource.IsEmpty()) {
- return;
- }
- MarkChanged();
- Maybe<MutexAutoLock> lock;
- sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
- if (!image) {
- return;
- }
- SkRect destRect = RectToSkRect(aDest);
- SkRect sourceRect = RectToSkRect(aSource);
- bool forceGroup = SkImageIsMask(image) &&
- aOptions.mCompositionOp != CompositionOp::OP_OVER;
- AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest, forceGroup);
- if (aSurfOptions.mSamplingFilter == SamplingFilter::POINT) {
- paint.mPaint.setFilterQuality(kNone_SkFilterQuality);
- }
- mCanvas->drawImageRect(image, sourceRect, destRect, &paint.mPaint);
- }
- DrawTargetType
- DrawTargetSkia::GetType() const
- {
- #ifdef USE_SKIA_GPU
- if (mGrContext) {
- return DrawTargetType::HARDWARE_RASTER;
- }
- #endif
- return DrawTargetType::SOFTWARE_RASTER;
- }
- void
- DrawTargetSkia::DrawFilter(FilterNode *aNode,
- const Rect &aSourceRect,
- const Point &aDestPoint,
- const DrawOptions &aOptions)
- {
- FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(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->GetSize().IsEmpty()) {
- return;
- }
- MarkChanged();
- Maybe<MutexAutoLock> lock;
- sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
- if (!image) {
- return;
- }
- mCanvas->save();
- mCanvas->resetMatrix();
- SkPaint paint;
- paint.setBlendMode(GfxOpToSkiaOp(aOperator));
- // bug 1201272
- // We can't use the SkDropShadowImageFilter here because it applies the xfer
- // mode first to render the bitmap to a temporary layer, and then implicitly
- // uses src-over to composite the resulting shadow.
- // The canvas spec, however, states that the composite op must be used to
- // composite the resulting shadow, so we must instead use a SkBlurImageFilter
- // to blur the image ourselves.
- SkPaint shadowPaint;
- shadowPaint.setBlendMode(GfxOpToSkiaOp(aOperator));
- auto shadowDest = IntPoint::Round(aDest + aOffset);
- SkBitmap blurMask;
- if (!UsingSkiaGPU() &&
- ExtractAlphaBitmap(image, &blurMask)) {
- // Prefer using our own box blur instead of Skia's when we're
- // not using the GPU. It currently performs much better than
- // SkBlurImageFilter or SkBlurMaskFilter on the CPU.
- AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
- int32_t(blurMask.rowBytes()),
- aSigma, aSigma);
- blurMask.lockPixels();
- blur.Blur(reinterpret_cast<uint8_t*>(blurMask.getPixels()));
- blurMask.unlockPixels();
- blurMask.notifyPixelsChanged();
- shadowPaint.setColor(ColorToSkColor(aColor, 1.0f));
- mCanvas->drawBitmap(blurMask, shadowDest.x, shadowDest.y, &shadowPaint);
- } else {
- sk_sp<SkImageFilter> blurFilter(SkBlurImageFilter::Make(aSigma, aSigma, nullptr));
- sk_sp<SkColorFilter> colorFilter(
- SkColorFilter::MakeModeFilter(ColorToSkColor(aColor, 1.0f), SkBlendMode::kSrcIn));
- shadowPaint.setImageFilter(blurFilter);
- shadowPaint.setColorFilter(colorFilter);
- mCanvas->drawImage(image, shadowDest.x, shadowDest.y, &shadowPaint);
- }
- // Composite the original image after the shadow
- auto dest = IntPoint::Round(aDest);
- mCanvas->drawImage(image, dest.x, dest.y, &paint);
- mCanvas->restore();
- }
- void
- DrawTargetSkia::FillRect(const Rect &aRect,
- const Pattern &aPattern,
- const DrawOptions &aOptions)
- {
- // The sprite blitting path in Skia can be faster than the shader blitter for
- // operators other than source (or source-over with opaque surface). So, when
- // possible/beneficial, route to DrawSurface which will use the sprite blitter.
- if (aPattern.GetType() == PatternType::SURFACE &&
- aOptions.mCompositionOp != CompositionOp::OP_SOURCE) {
- const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
- // Verify there is a valid surface and a pattern matrix without skew.
- if (pat.mSurface &&
- (aOptions.mCompositionOp != CompositionOp::OP_OVER ||
- GfxFormatToSkiaAlphaType(pat.mSurface->GetFormat()) != kOpaque_SkAlphaType) &&
- !pat.mMatrix.HasNonAxisAlignedTransform()) {
- // Bound the sampling to smaller of the bounds or the sampling rect.
- IntRect srcRect(IntPoint(0, 0), pat.mSurface->GetSize());
- if (!pat.mSamplingRect.IsEmpty()) {
- srcRect = srcRect.Intersect(pat.mSamplingRect);
- }
- // Transform the destination rectangle by the inverse of the pattern
- // matrix so that it is in pattern space like the source rectangle.
- Rect patRect = aRect - pat.mMatrix.GetTranslation();
- patRect.Scale(1.0f / pat.mMatrix._11, 1.0f / pat.mMatrix._22);
- // Verify the pattern rectangle will not tile or clamp.
- if (!patRect.IsEmpty() && srcRect.Contains(RoundedOut(patRect))) {
- // The pattern is a surface with an axis-aligned source rectangle
- // fitting entirely in its bounds, so just treat it as a DrawSurface.
- DrawSurface(pat.mSurface, aRect, patRect,
- DrawSurfaceOptions(pat.mSamplingFilter),
- aOptions);
- return;
- }
- }
- }
- 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<const PathSkia*>(aPath);
- AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
- if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
- return;
- }
- if (!skiaPath->GetPath().isFinite()) {
- 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 || aPath->GetBackendType() != BackendType::SKIA) {
- return;
- }
- const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
- AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
- if (!skiaPath->GetPath().isFinite()) {
- return;
- }
- mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
- }
- bool
- DrawTargetSkia::ShouldLCDRenderText(FontType aFontType, AntialiasMode aAntialiasMode)
- {
- // For non-opaque surfaces, only allow subpixel AA if explicitly permitted.
- if (!IsOpaque(mFormat) && !mPermitSubpixelAA) {
- return false;
- }
- if (aAntialiasMode == AntialiasMode::DEFAULT) {
- switch (aFontType) {
- case FontType::MAC:
- case FontType::GDI:
- case FontType::DWRITE:
- case FontType::FONTCONFIG:
- return true;
- default:
- // TODO: Figure out what to do for the other platforms.
- return false;
- }
- }
- return (aAntialiasMode == AntialiasMode::SUBPIXEL);
- }
- #ifdef MOZ_WIDGET_COCOA
- class CGClipApply : public SkCanvas::ClipVisitor {
- public:
- explicit CGClipApply(CGContextRef aCGContext)
- : mCG(aCGContext) {}
- void clipRect(const SkRect& aRect, SkCanvas::ClipOp op, bool antialias) override {
- CGRect rect = CGRectMake(aRect.x(), aRect.y(), aRect.width(), aRect.height());
- CGContextClipToRect(mCG, rect);
- }
- void clipRRect(const SkRRect& rrect, SkCanvas::ClipOp op, bool antialias) override {
- SkPath path;
- path.addRRect(rrect);
- clipPath(path, op, antialias);
- }
- void clipPath(const SkPath& aPath, SkCanvas::ClipOp, bool antialias) override {
- SkPath::Iter iter(aPath, true);
- SkPoint source[4];
- SkPath::Verb verb;
- RefPtr<PathBuilderCG> pathBuilder =
- new PathBuilderCG(GetFillRule(aPath.getFillType()));
- while ((verb = iter.next(source)) != SkPath::kDone_Verb) {
- switch (verb) {
- case SkPath::kMove_Verb:
- {
- SkPoint dest = source[0];
- pathBuilder->MoveTo(Point(dest.fX, dest.fY));
- break;
- }
- case SkPath::kLine_Verb:
- {
- // The first point should be the end point of whatever
- // verb we got to get here.
- SkPoint second = source[1];
- pathBuilder->LineTo(Point(second.fX, second.fY));
- break;
- }
- case SkPath::kQuad_Verb:
- {
- SkPoint second = source[1];
- SkPoint third = source[2];
- pathBuilder->QuadraticBezierTo(Point(second.fX, second.fY),
- Point(third.fX, third.fY));
- break;
- }
- case SkPath::kCubic_Verb:
- {
- SkPoint second = source[1];
- SkPoint third = source[2];
- SkPoint fourth = source[2];
- pathBuilder->BezierTo(Point(second.fX, second.fY),
- Point(third.fX, third.fY),
- Point(fourth.fX, fourth.fY));
- break;
- }
- case SkPath::kClose_Verb:
- {
- pathBuilder->Close();
- break;
- }
- default:
- {
- SkDEBUGFAIL("unknown verb");
- break;
- }
- } // end switch
- } // end while
- RefPtr<Path> path = pathBuilder->Finish();
- PathCG* cgPath = static_cast<PathCG*>(path.get());
- // Weirdly, CoreGraphics clips empty paths as all shown
- // but empty rects as all clipped. We detect this situation and
- // workaround it appropriately
- if (CGPathIsEmpty(cgPath->GetPath())) {
- CGContextClipToRect(mCG, CGRectZero);
- return;
- }
- CGContextBeginPath(mCG);
- CGContextAddPath(mCG, cgPath->GetPath());
- if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD) {
- CGContextEOClip(mCG);
- } else {
- CGContextClip(mCG);
- }
- }
- private:
- CGContextRef mCG;
- };
- static inline CGAffineTransform
- GfxMatrixToCGAffineTransform(const Matrix &m)
- {
- CGAffineTransform t;
- t.a = m._11;
- t.b = m._12;
- t.c = m._21;
- t.d = m._22;
- t.tx = m._31;
- t.ty = m._32;
- return t;
- }
- /***
- * We have to do a lot of work to draw glyphs with CG because
- * CG assumes that the origin of rects are in the bottom left
- * while every other DrawTarget assumes the top left is the origin.
- * This means we have to transform the CGContext to have rects
- * actually be applied in top left fashion. We do this by:
- *
- * 1) Translating the context up by the height of the canvas
- * 2) Flipping the context by the Y axis so it's upside down.
- *
- * These two transforms put the origin in the top left.
- * Transforms are better understood thinking about them from right to left order (mathematically).
- *
- * Consider a point we want to draw at (0, 10) in normal cartesian planes with
- * a box of (100, 100). in CG terms, this would be at (0, 10).
- * Positive Y values point up.
- * In our DrawTarget terms, positive Y values point down, so (0, 10) would be
- * at (0, 90) in cartesian plane terms. That means our point at (0, 10) in DrawTarget
- * terms should end up at (0, 90). How does this work with the current transforms?
- *
- * Going right to left with the transforms, a CGPoint of (0, 10) has cartesian coordinates
- * of (0, 10). The first flip of the Y axis puts the point now at (0, -10);
- * Next, we translate the context up by the size of the canvas (Positive Y values go up in CG
- * coordinates but down in our draw target coordinates). Since our canvas size is (100, 100),
- * the resulting coordinate becomes (0, 90), which is what we expect from our DrawTarget code.
- * These two transforms put the CG context equal to what every other DrawTarget expects.
- *
- * Next, we need two more transforms for actual text. IF we left the transforms as is,
- * the text would be drawn upside down, so we need another flip of the Y axis
- * to draw the text right side up. However, with only the flip, the text would be drawn
- * in the wrong place. Thus we also have to invert the Y position of the glyphs to get them
- * in the right place.
- *
- * Thus we have the following transforms:
- * 1) Translation of the context up
- * 2) Flipping the context around the Y axis
- * 3) Flipping the context around the Y axis
- * 4) Inverting the Y position of each glyph
- *
- * We cannot cancel out (2) and (3) as we have to apply the clips and transforms
- * of DrawTargetSkia between (2) and (3).
- *
- * Consider the example letter P, drawn at (0, 20) in CG coordinates in a (100, 100) rect.
- * Again, going right to left of the transforms. We'd get:
- *
- * 1) The letter P drawn at (0, -20) due to the inversion of the Y axis
- * 2) The letter P upside down (b) at (0, 20) due to the second flip
- * 3) The letter P right side up at (0, -20) due to the first flip
- * 4) The letter P right side up at (0, 80) due to the translation
- *
- * tl;dr - CGRects assume origin is bottom left, DrawTarget rects assume top left.
- */
- static bool
- SetupCGContext(DrawTargetSkia* aDT,
- CGContextRef aCGContext,
- sk_sp<SkCanvas> aCanvas)
- {
- // DrawTarget expects the origin to be at the top left, but CG
- // expects it to be at the bottom left. Transform to set the origin to
- // the top left. Have to set this before we do anything else.
- // This is transform (1) up top
- CGContextTranslateCTM(aCGContext, 0, aDT->GetSize().height);
- // Transform (2) from the comments.
- CGContextScaleCTM(aCGContext, 1, -1);
- // Want to apply clips BEFORE the transform since the transform
- // will apply to the clips we apply.
- // CGClipApply applies clips in device space, so it would be a mistake
- // to transform these clips.
- CGClipApply clipApply(aCGContext);
- aCanvas->replayClips(&clipApply);
- CGContextConcatCTM(aCGContext, GfxMatrixToCGAffineTransform(aDT->GetTransform()));
- return true;
- }
- static bool
- SetupCGGlyphs(CGContextRef aCGContext,
- const GlyphBuffer& aBuffer,
- Vector<CGGlyph,32>& aGlyphs,
- Vector<CGPoint,32>& aPositions)
- {
- // Flip again so we draw text in right side up. Transform (3) from the top
- CGContextScaleCTM(aCGContext, 1, -1);
- if (!aGlyphs.resizeUninitialized(aBuffer.mNumGlyphs) ||
- !aPositions.resizeUninitialized(aBuffer.mNumGlyphs)) {
- gfxDevCrash(LogReason::GlyphAllocFailedCG) << "glyphs/positions allocation failed";
- return false;
- }
- for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
- aGlyphs[i] = aBuffer.mGlyphs[i].mIndex;
- // Flip the y coordinates so that text ends up in the right spot after the (3) flip
- // Inversion from (4) in the comments.
- aPositions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
- -aBuffer.mGlyphs[i].mPosition.y);
- }
- return true;
- }
- // End long comment about transforms. SetupCGContext and SetupCGGlyphs should stay
- // next to each other.
- // The context returned from this method will have the origin
- // in the top left and will hvae applied all the neccessary clips
- // and transforms to the CGContext. See the comment above
- // SetupCGContext.
- CGContextRef
- DrawTargetSkia::BorrowCGContext(const DrawOptions &aOptions)
- {
- int32_t stride;
- SurfaceFormat format;
- IntSize size;
- uint8_t* aSurfaceData = nullptr;
- if (!LockBits(&aSurfaceData, &size, &stride, &format)) {
- NS_WARNING("Could not lock skia bits to wrap CG around");
- return nullptr;
- }
- if ((aSurfaceData == mCanvasData) && mCG && (mCGSize == size)) {
- // If our canvas data still points to the same data,
- // we can reuse the CG Context
- CGContextSaveGState(mCG);
- CGContextSetAlpha(mCG, aOptions.mAlpha);
- SetupCGContext(this, mCG, mCanvas);
- return mCG;
- }
- if (!mColorSpace) {
- mColorSpace = (format == SurfaceFormat::A8) ?
- CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB();
- }
- if (mCG) {
- // Release the old CG context since it's no longer valid.
- CGContextRelease(mCG);
- }
- mCanvasData = aSurfaceData;
- mCGSize = size;
- uint32_t bitmapInfo = (format == SurfaceFormat::A8) ?
- kCGImageAlphaOnly :
- kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
- mCG = CGBitmapContextCreateWithData(mCanvasData,
- mCGSize.width,
- mCGSize.height,
- 8, /* bits per component */
- stride,
- mColorSpace,
- bitmapInfo,
- NULL, /* Callback when released */
- NULL);
- if (!mCG) {
- ReleaseBits(mCanvasData);
- NS_WARNING("Could not create bitmap around skia data\n");
- return nullptr;
- }
- CGContextSetAlpha(mCG, aOptions.mAlpha);
- CGContextSetShouldAntialias(mCG, aOptions.mAntialiasMode != AntialiasMode::NONE);
- CGContextSetShouldSmoothFonts(mCG, true);
- CGContextSetTextDrawingMode(mCG, kCGTextFill);
- CGContextSaveGState(mCG);
- SetupCGContext(this, mCG, mCanvas);
- return mCG;
- }
- void
- DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext)
- {
- MOZ_ASSERT(aCGContext == mCG);
- ReleaseBits(mCanvasData);
- CGContextRestoreGState(aCGContext);
- }
- CGContextRef
- BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT)
- {
- DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
- return skiaDT->BorrowCGContext(DrawOptions());
- }
- void
- BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg)
- {
- DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
- skiaDT->ReturnCGContext(cg);
- return;
- }
- static void
- SetFontColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace, const Pattern& aPattern)
- {
- const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
- CGColorRef textColor = ColorToCGColor(aColorSpace, color);
- CGContextSetFillColorWithColor(aCGContext, textColor);
- CGColorRelease(textColor);
- }
- /***
- * We need this to support subpixel AA text on OS X in two cases:
- * text in DrawTargets that are not opaque and text over vibrant backgrounds.
- * Skia normally doesn't support subpixel AA text on transparent backgrounds.
- * To get around this, we have to wrap the Skia bytes with a CGContext and ask
- * CG to draw the text.
- * In vibrancy cases, we have to use a private API,
- * CGContextSetFontSmoothingBackgroundColor, which sets the expected
- * background color the text will draw onto so that CG can render the text
- * properly. After that, we have to go back and fixup the pixels
- * such that their alpha values are correct.
- */
- bool
- DrawTargetSkia::FillGlyphsWithCG(ScaledFont *aFont,
- const GlyphBuffer &aBuffer,
- const Pattern &aPattern,
- const DrawOptions &aOptions,
- const GlyphRenderingOptions *aRenderingOptions)
- {
- MOZ_ASSERT(aFont->GetType() == FontType::MAC);
- MOZ_ASSERT(aPattern.GetType() == PatternType::COLOR);
- CGContextRef cgContext = BorrowCGContext(aOptions);
- if (!cgContext) {
- return false;
- }
- Vector<CGGlyph,32> glyphs;
- Vector<CGPoint,32> positions;
- if (!SetupCGGlyphs(cgContext, aBuffer, glyphs, positions)) {
- ReturnCGContext(cgContext);
- return false;
- }
- SetFontSmoothingBackgroundColor(cgContext, mColorSpace, aRenderingOptions);
- SetFontColor(cgContext, mColorSpace, aPattern);
- ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
- if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
- ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, glyphs.begin(),
- positions.begin(),
- aBuffer.mNumGlyphs, cgContext);
- } else {
- CGContextSetFont(cgContext, macFont->mFont);
- CGContextSetFontSize(cgContext, macFont->mSize);
- CGContextShowGlyphsAtPositions(cgContext, glyphs.begin(), positions.begin(),
- aBuffer.mNumGlyphs);
- }
- // Calculate the area of the text we just drew
- CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs];
- CTFontGetBoundingRectsForGlyphs(macFont->mCTFont, kCTFontDefaultOrientation,
- glyphs.begin(), bboxes, aBuffer.mNumGlyphs);
- CGRect extents = ComputeGlyphsExtents(bboxes, positions.begin(), aBuffer.mNumGlyphs, 1.0f);
- delete[] bboxes;
- CGAffineTransform cgTransform = CGContextGetCTM(cgContext);
- extents = CGRectApplyAffineTransform(extents, cgTransform);
- // Have to round it out to ensure we fully cover all pixels
- Rect rect(extents.origin.x, extents.origin.y, extents.size.width, extents.size.height);
- rect.RoundOut();
- extents = CGRectMake(rect.x, rect.y, rect.width, rect.height);
- EnsureValidPremultipliedData(cgContext, extents);
- ReturnCGContext(cgContext);
- return true;
- }
- static bool
- HasFontSmoothingBackgroundColor(const GlyphRenderingOptions* aRenderingOptions)
- {
- // This should generally only be true if we have a popup context menu
- if (aRenderingOptions && aRenderingOptions->GetType() == FontType::MAC) {
- Color fontSmoothingBackgroundColor =
- static_cast<const GlyphRenderingOptionsCG*>(aRenderingOptions)->FontSmoothingBackgroundColor();
- return fontSmoothingBackgroundColor.a > 0;
- }
- return false;
- }
- static bool
- ShouldUseCGToFillGlyphs(const GlyphRenderingOptions* aOptions, const Pattern& aPattern)
- {
- return HasFontSmoothingBackgroundColor(aOptions) &&
- aPattern.GetType() == PatternType::COLOR;
- }
- #endif
- static bool
- CanDrawFont(ScaledFont* aFont)
- {
- switch (aFont->GetType()) {
- case FontType::SKIA:
- case FontType::CAIRO:
- case FontType::FONTCONFIG:
- case FontType::MAC:
- case FontType::GDI:
- case FontType::DWRITE:
- return true;
- default:
- return false;
- }
- }
- void
- DrawTargetSkia::FillGlyphs(ScaledFont *aFont,
- const GlyphBuffer &aBuffer,
- const Pattern &aPattern,
- const DrawOptions &aOptions,
- const GlyphRenderingOptions *aRenderingOptions)
- {
- if (!CanDrawFont(aFont)) {
- return;
- }
- MarkChanged();
- #ifdef MOZ_WIDGET_COCOA
- if (ShouldUseCGToFillGlyphs(aRenderingOptions, aPattern)) {
- if (FillGlyphsWithCG(aFont, aBuffer, aPattern, aOptions, aRenderingOptions)) {
- return;
- }
- }
- #endif
- ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
- SkTypeface* typeface = skiaFont->GetSkTypeface();
- if (!typeface) {
- return;
- }
- AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
- AntialiasMode aaMode = aFont->GetDefaultAAMode();
- if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
- aaMode = aOptions.mAntialiasMode;
- }
- bool aaEnabled = aaMode != AntialiasMode::NONE;
- paint.mPaint.setAntiAlias(aaEnabled);
- paint.mPaint.setTypeface(sk_ref_sp(typeface));
- paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize));
- paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- bool shouldLCDRenderText = ShouldLCDRenderText(aFont->GetType(), aaMode);
- paint.mPaint.setLCDRenderText(shouldLCDRenderText);
- bool useSubpixelText = true;
- switch (aFont->GetType()) {
- case FontType::SKIA:
- case FontType::CAIRO:
- case FontType::FONTCONFIG:
- // SkFontHost_cairo does not support subpixel text positioning,
- // so only enable it for other font hosts.
- useSubpixelText = false;
- break;
- case FontType::MAC:
- if (aaMode == AntialiasMode::GRAY) {
- // Normally, Skia enables LCD FontSmoothing which creates thicker fonts
- // and also enables subpixel AA. CoreGraphics without font smoothing
- // explicitly creates thinner fonts and grayscale AA.
- // CoreGraphics doesn't support a configuration that produces thicker
- // fonts with grayscale AA as LCD Font Smoothing enables or disables both.
- // However, Skia supports it by enabling font smoothing (producing subpixel AA)
- // and converts it to grayscale AA. Since Skia doesn't support subpixel AA on
- // transparent backgrounds, we still want font smoothing for the thicker fonts,
- // even if it is grayscale AA.
- //
- // With explicit Grayscale AA (from -moz-osx-font-smoothing:grayscale),
- // we want to have grayscale AA with no smoothing at all. This means
- // disabling the LCD font smoothing behaviour.
- // To accomplish this we have to explicitly disable hinting,
- // and disable LCDRenderText.
- paint.mPaint.setHinting(SkPaint::kNo_Hinting);
- }
- break;
- case FontType::GDI:
- {
- if (!shouldLCDRenderText && aaEnabled) {
- // If we have non LCD GDI text, render the fonts as cleartype and convert them
- // to grayscale. This seems to be what Chrome and IE are doing on Windows 7.
- // This also applies if cleartype is disabled system wide.
- paint.mPaint.setFlags(paint.mPaint.getFlags() | SkPaint::kGenA8FromLCD_Flag);
- }
- break;
- }
- #ifdef XP_WIN
- case FontType::DWRITE:
- {
- ScaledFontDWrite* dwriteFont = static_cast<ScaledFontDWrite*>(aFont);
- paint.mPaint.setEmbeddedBitmapText(dwriteFont->UseEmbeddedBitmaps());
- if (dwriteFont->ForceGDIMode()) {
- paint.mPaint.setEmbeddedBitmapText(true);
- useSubpixelText = false;
- }
- break;
- }
- #endif
- default:
- break;
- }
- paint.mPaint.setSubpixelText(useSubpixelText);
- std::vector<uint16_t> indices;
- std::vector<SkPoint> 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);
- Maybe<MutexAutoLock> lock;
- SkPaint maskPaint;
- SetPaintPattern(maskPaint, aMask, lock);
- SkLayerRasterizer::Builder builder;
- builder.addLayer(maskPaint);
- sk_sp<SkLayerRasterizer> raster(builder.detach());
- paint.mPaint.setRasterizer(raster);
- mCanvas->drawPaint(paint.mPaint);
- }
- void
- DrawTargetSkia::MaskSurface(const Pattern &aSource,
- SourceSurface *aMask,
- Point aOffset,
- const DrawOptions &aOptions)
- {
- MarkChanged();
- AutoPaintSetup paint(mCanvas.get(), aOptions, aSource, nullptr, -aOffset);
- Maybe<MutexAutoLock> lock;
- sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask, lock);
- if (!alphaMask) {
- gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask";
- return;
- }
- mCanvas->drawImage(alphaMask, aOffset.x, aOffset.y, &paint.mPaint);
- }
- bool
- DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
- {
- // Composite the 3D transform with the DT's transform.
- Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
- if (fullMat.IsSingular()) {
- return false;
- }
- // Transform the surface bounds and clip to this DT.
- IntRect xformBounds =
- RoundedOut(
- fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
- Rect(Point(0, 0), Size(GetSize()))));
- if (xformBounds.IsEmpty()) {
- return true;
- }
- // Offset the matrix by the transformed origin.
- fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
- // Read in the source data.
- Maybe<MutexAutoLock> lock;
- sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
- if (!srcImage) {
- return true;
- }
- // Set up an intermediate destination surface only the size of the transformed bounds.
- // Try to pass through the source's format unmodified in both the BGRA and ARGB cases.
- RefPtr<DataSourceSurface> dstSurf =
- Factory::CreateDataSourceSurface(xformBounds.Size(),
- !srcImage->isOpaque() ?
- aSurface->GetFormat() : SurfaceFormat::A8R8G8B8_UINT32,
- true);
- if (!dstSurf) {
- return false;
- }
- sk_sp<SkCanvas> dstCanvas(
- SkCanvas::NewRasterDirect(
- SkImageInfo::Make(xformBounds.width, xformBounds.height,
- GfxFormatToSkiaColorType(dstSurf->GetFormat()),
- kPremul_SkAlphaType),
- dstSurf->GetData(), dstSurf->Stride()));
- if (!dstCanvas) {
- return false;
- }
- // Do the transform.
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setFilterQuality(kLow_SkFilterQuality);
- paint.setBlendMode(SkBlendMode::kSrc);
- SkMatrix xform;
- GfxMatrixToSkiaMatrix(fullMat, xform);
- dstCanvas->setMatrix(xform);
- dstCanvas->drawImage(srcImage, 0, 0, &paint);
- dstCanvas->flush();
- // Temporarily reset the DT's transform, since it has already been composed above.
- Matrix origTransform = mTransform;
- SetTransform(Matrix());
- // Draw the transformed surface within the transformed bounds.
- DrawSurface(dstSurf, Rect(xformBounds), Rect(Point(0, 0), Size(xformBounds.Size())));
- SetTransform(origTransform);
- return true;
- }
- bool
- DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
- {
- if (aMatrix.IsSingular()) {
- return false;
- }
- MarkChanged();
- Maybe<MutexAutoLock> lock;
- sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
- if (!image) {
- return true;
- }
- mCanvas->save();
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setFilterQuality(kLow_SkFilterQuality);
- SkMatrix xform;
- GfxMatrixToSkiaMatrix(aMatrix, xform);
- mCanvas->concat(xform);
- mCanvas->drawImage(image, 0, 0, &paint);
- mCanvas->restore();
- return true;
- }
- already_AddRefed<SourceSurface>
- DrawTargetSkia::CreateSourceSurfaceFromData(unsigned char *aData,
- const IntSize &aSize,
- int32_t aStride,
- SurfaceFormat aFormat) const
- {
- RefPtr<SourceSurfaceSkia> 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.forget();
- }
- already_AddRefed<DrawTarget>
- DrawTargetSkia::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
- {
- RefPtr<DrawTargetSkia> target = new DrawTargetSkia();
- #ifdef USE_SKIA_GPU
- if (UsingSkiaGPU()) {
- // Try to create a GPU draw target first if we're currently using the GPU.
- // Mark the DT as cached so that shadow DTs, extracted subrects, and similar can be reused.
- if (target->InitWithGrContext(mGrContext.get(), aSize, aFormat, true)) {
- return target.forget();
- }
- // Otherwise, just fall back to a software draw target.
- }
- #endif
- #ifdef DEBUG
- if (!IsBackedByPixels(mCanvas.get())) {
- // If our canvas is backed by vector storage such as PDF then we want to
- // create a new DrawTarget with similar storage to avoid losing fidelity
- // (fidelity will be lost if the returned DT is Snapshot()'ed and drawn
- // back onto us since a raster will be drawn instead of vector commands).
- NS_WARNING("Not backed by pixels - we need to handle PDF backed SkCanvas");
- }
- #endif
- if (!target->Init(aSize, aFormat)) {
- return nullptr;
- }
- return target.forget();
- }
- bool
- DrawTargetSkia::UsingSkiaGPU() const
- {
- #ifdef USE_SKIA_GPU
- return !!mGrContext;
- #else
- return false;
- #endif
- }
- #ifdef USE_SKIA_GPU
- already_AddRefed<SourceSurface>
- DrawTargetSkia::OptimizeGPUSourceSurface(SourceSurface *aSurface) const
- {
- // Check if the underlying SkImage already has an associated GrTexture.
- Maybe<MutexAutoLock> lock;
- sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
- if (!image || image->isTextureBacked()) {
- RefPtr<SourceSurface> surface(aSurface);
- return surface.forget();
- }
- // Upload the SkImage to a GrTexture otherwise.
- sk_sp<SkImage> texture = image->makeTextureImage(mGrContext.get());
- if (texture) {
- // Create a new SourceSurfaceSkia whose SkImage contains the GrTexture.
- RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
- if (surface->InitFromImage(texture, aSurface->GetFormat())) {
- return surface.forget();
- }
- }
- // The data was too big to fit in a GrTexture.
- if (aSurface->GetType() == SurfaceType::SKIA) {
- // It is already a Skia source surface, so just reuse it as-is.
- RefPtr<SourceSurface> surface(aSurface);
- return surface.forget();
- }
- // Wrap it in a Skia source surface so that can do tiled uploads on-demand.
- RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
- surface->InitFromImage(image);
- return surface.forget();
- }
- #endif
- already_AddRefed<SourceSurface>
- DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha(SourceSurface *aSurface) const
- {
- #ifdef USE_SKIA_GPU
- if (UsingSkiaGPU()) {
- return OptimizeGPUSourceSurface(aSurface);
- }
- #endif
- if (aSurface->GetType() == SurfaceType::SKIA) {
- RefPtr<SourceSurface> surface(aSurface);
- return surface.forget();
- }
- RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
- // For plugins, GDI can sometimes just write 0 to the alpha channel
- // even for RGBX formats. In this case, we have to manually write
- // the alpha channel to make Skia happy with RGBX and in case GDI
- // writes some bad data. Luckily, this only happens on plugins.
- WriteRGBXFormat(dataSurface->GetData(), dataSurface->GetSize(),
- dataSurface->Stride(), dataSurface->GetFormat());
- return dataSurface.forget();
- }
- already_AddRefed<SourceSurface>
- DrawTargetSkia::OptimizeSourceSurface(SourceSurface *aSurface) const
- {
- #ifdef USE_SKIA_GPU
- if (UsingSkiaGPU()) {
- return OptimizeGPUSourceSurface(aSurface);
- }
- #endif
- if (aSurface->GetType() == SurfaceType::SKIA) {
- RefPtr<SourceSurface> surface(aSurface);
- return surface.forget();
- }
- // If we're not using skia-gl then drawing doesn't require any
- // uploading, so any data surface is fine. Call GetDataSurface
- // to trigger any required readback so that it only happens
- // once.
- RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
- MOZ_ASSERT(VerifyRGBXFormat(dataSurface->GetData(), dataSurface->GetSize(),
- dataSurface->Stride(), dataSurface->GetFormat()));
- return dataSurface.forget();
- }
- already_AddRefed<SourceSurface>
- DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
- {
- #ifdef USE_SKIA_GPU
- if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE && UsingSkiaGPU()) {
- // Wrap the OpenGL texture id in a Skia texture handle.
- GrBackendTextureDesc texDesc;
- texDesc.fWidth = aSurface.mSize.width;
- texDesc.fHeight = aSurface.mSize.height;
- texDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
- texDesc.fConfig = GfxFormatToGrConfig(aSurface.mFormat);
- GrGLTextureInfo texInfo;
- texInfo.fTarget = LOCAL_GL_TEXTURE_2D;
- texInfo.fID = (GrGLuint)(uintptr_t)aSurface.mSurface;
- texDesc.fTextureHandle = reinterpret_cast<GrBackendObject>(&texInfo);
- sk_sp<SkImage> texture =
- SkImage::MakeFromAdoptedTexture(mGrContext.get(), texDesc,
- GfxFormatToSkiaAlphaType(aSurface.mFormat));
- RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
- if (texture && newSurf->InitFromImage(texture, aSurface.mFormat)) {
- return newSurf.forget();
- }
- return nullptr;
- }
- #endif
- return nullptr;
- }
- void
- DrawTargetSkia::CopySurface(SourceSurface *aSurface,
- const IntRect& aSourceRect,
- const IntPoint &aDestination)
- {
- MarkChanged();
- Maybe<MutexAutoLock> lock;
- sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
- if (!image) {
- return;
- }
- mCanvas->save();
- mCanvas->setMatrix(SkMatrix::MakeTrans(SkIntToScalar(aDestination.x), SkIntToScalar(aDestination.y)));
- mCanvas->clipRect(SkRect::MakeIWH(aSourceRect.width, aSourceRect.height), kReplace_SkClipOp);
- SkPaint paint;
- if (!image->isOpaque()) {
- // Keep the xfermode as SOURCE_OVER for opaque bitmaps
- // http://code.google.com/p/skia/issues/detail?id=628
- paint.setBlendMode(SkBlendMode::kSrc);
- }
- // drawImage with A8 images ends up doing a mask operation
- // so we need to clear before
- if (SkImageIsMask(image)) {
- mCanvas->clear(SK_ColorTRANSPARENT);
- }
- mCanvas->drawImage(image, -SkIntToScalar(aSourceRect.x), -SkIntToScalar(aSourceRect.y), &paint);
- mCanvas->restore();
- }
- bool
- DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat)
- {
- if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
- return false;
- }
- // we need to have surfaces that have a stride aligned to 4 for interop with cairo
- SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
- size_t stride = SkAlign4(info.minRowBytes());
- mSurface = SkSurface::MakeRaster(info, stride, nullptr);
- if (!mSurface) {
- return false;
- }
- mSize = aSize;
- mFormat = aFormat;
- mCanvas = sk_ref_sp(mSurface->getCanvas());
- if (info.isOpaque()) {
- mCanvas->clear(SK_ColorBLACK);
- }
- return true;
- }
- bool
- DrawTargetSkia::Init(SkCanvas* aCanvas)
- {
- mCanvas = sk_ref_sp(aCanvas);
- SkImageInfo imageInfo = mCanvas->imageInfo();
- // If the canvas is backed by pixels we clear it to be on the safe side. If
- // it's not (for example, for PDF output) we don't.
- if (IsBackedByPixels(mCanvas.get())) {
- SkColor clearColor = imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT;
- mCanvas->clear(clearColor);
- }
- SkISize size = mCanvas->getBaseLayerSize();
- mSize.width = size.width();
- mSize.height = size.height();
- mFormat = SkiaColorTypeToGfxFormat(imageInfo.colorType(),
- imageInfo.alphaType());
- return true;
- }
- #ifdef USE_SKIA_GPU
- /** Indicating a DT should be cached means that space will be reserved in Skia's cache
- * for the render target at creation time, with any unused resources exceeding the cache
- * limits being purged. When the DT is freed, it will then be guaranteed to be kept around
- * for subsequent allocations until it gets incidentally purged.
- *
- * If it is not marked as cached, no space will be purged to make room for the render
- * target in the cache. When the DT is freed, If there is space within the resource limits
- * it may be added to the cache, otherwise it will be freed immediately if the cache is
- * already full.
- *
- * If you want to ensure that the resources will be kept around for reuse, it is better
- * to mark them as cached. Such resources should be short-lived to ensure they don't
- * permanently tie up cache resource limits. Long-lived resources should generally be
- * left as uncached.
- *
- * In neither case will cache resource limits affect whether the resource allocation
- * succeeds. The amount of in-use GPU resources is allowed to exceed the size of the cache.
- * Thus, only hard GPU out-of-memory conditions will cause resource allocation to fail.
- */
- bool
- DrawTargetSkia::InitWithGrContext(GrContext* aGrContext,
- const IntSize &aSize,
- SurfaceFormat aFormat,
- bool aCached)
- {
- MOZ_ASSERT(aGrContext, "null GrContext");
- if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
- return false;
- }
- // Create a GPU rendertarget/texture using the supplied GrContext.
- // NewRenderTarget also implicitly clears the underlying texture on creation.
- mSurface =
- SkSurface::MakeRenderTarget(aGrContext,
- SkBudgeted(aCached),
- MakeSkiaImageInfo(aSize, aFormat));
- if (!mSurface) {
- return false;
- }
- mGrContext = sk_ref_sp(aGrContext);
- mSize = aSize;
- mFormat = aFormat;
- mCanvas = sk_ref_sp(mSurface->getCanvas());
- return true;
- }
- #endif
- bool
- DrawTargetSkia::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized)
- {
- MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) ||
- aUninitialized || VerifyRGBXFormat(aData, aSize, aStride, aFormat));
- mSurface = SkSurface::MakeRasterDirect(MakeSkiaImageInfo(aSize, aFormat), aData, aStride);
- if (!mSurface) {
- return false;
- }
- mSize = aSize;
- mFormat = aFormat;
- mCanvas = sk_ref_sp(mSurface->getCanvas());
- return true;
- }
- void
- DrawTargetSkia::SetTransform(const Matrix& aTransform)
- {
- SkMatrix mat;
- GfxMatrixToSkiaMatrix(aTransform, mat);
- mCanvas->setMatrix(mat);
- mTransform = aTransform;
- }
- void*
- DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType)
- {
- #ifdef USE_SKIA_GPU
- if (aType == NativeSurfaceType::OPENGL_TEXTURE && mSurface) {
- GrBackendObject handle = mSurface->getTextureHandle(SkSurface::kFlushRead_BackendHandleAccess);
- if (handle) {
- return (void*)(uintptr_t)reinterpret_cast<GrGLTextureInfo *>(handle)->fID;
- }
- }
- #endif
- return nullptr;
- }
- already_AddRefed<PathBuilder>
- DrawTargetSkia::CreatePathBuilder(FillRule aFillRule) const
- {
- return MakeAndAddRef<PathBuilderSkia>(aFillRule);
- }
- void
- DrawTargetSkia::ClearRect(const Rect &aRect)
- {
- MarkChanged();
- mCanvas->save();
- mCanvas->clipRect(RectToSkRect(aRect), kIntersect_SkClipOp, true);
- SkColor clearColor = (mFormat == SurfaceFormat::B8G8R8X8) ? SK_ColorBLACK : SK_ColorTRANSPARENT;
- mCanvas->clear(clearColor);
- mCanvas->restore();
- }
- void
- DrawTargetSkia::PushClip(const Path *aPath)
- {
- if (aPath->GetBackendType() != BackendType::SKIA) {
- return;
- }
- const PathSkia *skiaPath = static_cast<const PathSkia*>(aPath);
- mCanvas->save();
- mCanvas->clipPath(skiaPath->GetPath(), kIntersect_SkClipOp, true);
- }
- void
- DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
- {
- // Build a region by unioning all the rects together.
- SkRegion region;
- for (uint32_t i = 0; i < aCount; i++) {
- region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op);
- }
- // Clip with the resulting region. clipRegion does not transform
- // this region by the current transform, unlike the other SkCanvas
- // clip methods, so it is just passed through in device-space.
- mCanvas->save();
- mCanvas->clipRegion(region, kIntersect_SkClipOp);
- }
- void
- DrawTargetSkia::PushClipRect(const Rect& aRect)
- {
- SkRect rect = RectToSkRect(aRect);
- mCanvas->save();
- mCanvas->clipRect(rect, kIntersect_SkClipOp, true);
- }
- void
- DrawTargetSkia::PopClip()
- {
- mCanvas->restore();
- }
- // Image filter that just passes the source through to the result unmodified.
- class CopyLayerImageFilter : public SkImageFilter
- {
- public:
- CopyLayerImageFilter()
- : SkImageFilter(nullptr, 0, nullptr)
- {}
- virtual sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source,
- const Context& ctx,
- SkIPoint* offset) const override {
- offset->set(0, 0);
- return sk_ref_sp(source);
- }
- SK_TO_STRING_OVERRIDE()
- SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(CopyLayerImageFilter)
- };
- sk_sp<SkFlattenable>
- CopyLayerImageFilter::CreateProc(SkReadBuffer& buffer)
- {
- SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
- return sk_make_sp<CopyLayerImageFilter>();
- }
- #ifndef SK_IGNORE_TO_STRING
- void
- CopyLayerImageFilter::toString(SkString* str) const
- {
- str->append("CopyLayerImageFilter: ()");
- }
- #endif
- void
- DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
- const Matrix& aMaskTransform, const IntRect& aBounds,
- bool aCopyBackground)
- {
- PushedLayer layer(GetPermitSubpixelAA(), aOpaque, aOpacity, aMask, aMaskTransform);
- mPushedLayers.push_back(layer);
- SkPaint paint;
- // If we have a mask, set the opacity to 0 so that SkCanvas::restore skips
- // implicitly drawing the layer so that we can properly mask it in PopLayer.
- paint.setAlpha(aMask ? 0 : ColorFloatToByte(aOpacity));
- SkRect bounds = IntRectToSkRect(aBounds);
- sk_sp<SkImageFilter> backdrop(aCopyBackground ? new CopyLayerImageFilter : nullptr);
- SkCanvas::SaveLayerRec saveRec(aBounds.IsEmpty() ? nullptr : &bounds,
- &paint,
- backdrop.get(),
- aOpaque ? SkCanvas::kIsOpaque_SaveLayerFlag : 0);
- mCanvas->saveLayer(saveRec);
- SetPermitSubpixelAA(aOpaque);
- #ifdef MOZ_WIDGET_COCOA
- CGContextRelease(mCG);
- mCG = nullptr;
- #endif
- }
- void
- DrawTargetSkia::PopLayer()
- {
- MarkChanged();
- MOZ_ASSERT(mPushedLayers.size());
- const PushedLayer& layer = mPushedLayers.back();
- if (layer.mMask) {
- // If we have a mask, take a reference to the top layer's device so that
- // we can mask it ourselves. This assumes we forced SkCanvas::restore to
- // skip implicitly drawing the layer.
- sk_sp<SkBaseDevice> layerDevice = sk_ref_sp(mCanvas->getTopDevice());
- SkIRect layerBounds = layerDevice->getGlobalBounds();
- sk_sp<SkImage> layerImage;
- SkPixmap layerPixmap;
- if (layerDevice->peekPixels(&layerPixmap)) {
- layerImage = SkImage::MakeFromRaster(layerPixmap, nullptr, nullptr);
- #ifdef USE_SKIA_GPU
- } else if (GrDrawContext* drawCtx = mCanvas->internal_private_accessTopLayerDrawContext()) {
- drawCtx->prepareForExternalIO();
- if (GrTexture* tex = drawCtx->accessRenderTarget()->asTexture()) {
- layerImage = sk_make_sp<SkImage_Gpu>(layerBounds.width(), layerBounds.height(),
- kNeedNewImageUniqueID,
- layerDevice->imageInfo().alphaType(),
- tex, nullptr, SkBudgeted::kNo);
- }
- #endif
- }
- // Restore the background with the layer's device left alive.
- mCanvas->restore();
- SkPaint paint;
- paint.setAlpha(ColorFloatToByte(layer.mOpacity));
- SkMatrix maskMat, layerMat;
- // Get the total transform affecting the mask, considering its pattern
- // transform and the current canvas transform.
- GfxMatrixToSkiaMatrix(layer.mMaskTransform, maskMat);
- maskMat.postConcat(mCanvas->getTotalMatrix());
- if (!maskMat.invert(&layerMat)) {
- gfxDebug() << *this << ": PopLayer() failed to invert mask transform";
- } else {
- // The layer should not be affected by the current canvas transform,
- // even though the mask is. So first we use the inverse of the transform
- // affecting the mask, then add back on the layer's origin.
- layerMat.preTranslate(layerBounds.x(), layerBounds.y());
- if (layerImage) {
- paint.setShader(layerImage->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &layerMat));
- } else {
- paint.setColor(SK_ColorTRANSPARENT);
- }
- Maybe<MutexAutoLock> lock;
- sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask, lock);
- if (!alphaMask) {
- gfxDebug() << *this << ": PopLayer() failed to extract alpha for mask";
- } else {
- mCanvas->save();
- // The layer may be smaller than the canvas size, so make sure drawing is
- // clipped to within the bounds of the layer.
- mCanvas->resetMatrix();
- mCanvas->clipRect(SkRect::Make(layerBounds));
- mCanvas->setMatrix(maskMat);
- mCanvas->drawImage(alphaMask, 0, 0, &paint);
- mCanvas->restore();
- }
- }
- } else {
- mCanvas->restore();
- }
- SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
- mPushedLayers.pop_back();
- #ifdef MOZ_WIDGET_COCOA
- CGContextRelease(mCG);
- mCG = nullptr;
- #endif
- }
- already_AddRefed<GradientStops>
- DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const
- {
- std::vector<GradientStop> stops;
- stops.resize(aNumStops);
- for (uint32_t i = 0; i < aNumStops; i++) {
- stops[i] = aStops[i];
- }
- std::stable_sort(stops.begin(), stops.end());
- return MakeAndAddRef<GradientStopsSkia>(stops, aNumStops, aExtendMode);
- }
- already_AddRefed<FilterNode>
- DrawTargetSkia::CreateFilter(FilterType aType)
- {
- return FilterNodeSoftware::Create(aType);
- }
- void
- DrawTargetSkia::MarkChanged()
- {
- if (mSnapshot) {
- mSnapshot->DrawTargetWillChange();
- mSnapshot = nullptr;
- // Handle copying of any image snapshots bound to the surface.
- if (mSurface) {
- mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
- }
- }
- }
- void
- DrawTargetSkia::SnapshotDestroyed()
- {
- mSnapshot = nullptr;
- }
- } // namespace gfx
- } // namespace mozilla
|