123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482 |
- /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * 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 "gfxUtils.h"
- #include "cairo.h"
- #include "gfxContext.h"
- #include "gfxEnv.h"
- #include "gfxImageSurface.h"
- #include "gfxPlatform.h"
- #include "gfxDrawable.h"
- #include "imgIEncoder.h"
- #include "libyuv.h"
- #include "mozilla/Base64.h"
- #include "mozilla/dom/ImageEncoder.h"
- #include "mozilla/dom/WorkerPrivate.h"
- #include "mozilla/dom/WorkerRunnable.h"
- #include "mozilla/gfx/2D.h"
- #include "mozilla/gfx/DataSurfaceHelpers.h"
- #include "mozilla/gfx/Logging.h"
- #include "mozilla/gfx/PathHelpers.h"
- #include "mozilla/Maybe.h"
- #include "mozilla/RefPtr.h"
- #include "mozilla/UniquePtrExtensions.h"
- #include "mozilla/Vector.h"
- #include "nsComponentManagerUtils.h"
- #include "nsIClipboardHelper.h"
- #include "nsIFile.h"
- #include "nsIGfxInfo.h"
- #include "nsIPresShell.h"
- #include "nsPresContext.h"
- #include "nsRegion.h"
- #include "nsServiceManagerUtils.h"
- #include "GeckoProfiler.h"
- #include "ImageContainer.h"
- #include "ImageRegion.h"
- #include "gfx2DGlue.h"
- #include "gfxPrefs.h"
- #ifdef XP_WIN
- #include "gfxWindowsPlatform.h"
- #endif
- using namespace mozilla;
- using namespace mozilla::image;
- using namespace mozilla::layers;
- using namespace mozilla::gfx;
- #include "DeprecatedPremultiplyTables.h"
- #undef compress
- #include "mozilla/Compression.h"
- using namespace mozilla::Compression;
- extern "C" {
- /**
- * Dump a raw image to the default log. This function is exported
- * from libxul, so it can be called from any library in addition to
- * (of course) from a debugger.
- *
- * Note: this helper currently assumes that all 2-bytepp images are
- * r5g6b5, and that all 4-bytepp images are r8g8b8a8.
- */
- NS_EXPORT
- void mozilla_dump_image(void* bytes, int width, int height, int bytepp,
- int strideBytes)
- {
- if (0 == strideBytes) {
- strideBytes = width * bytepp;
- }
- SurfaceFormat format;
- // TODO more flexible; parse string?
- switch (bytepp) {
- case 2:
- format = SurfaceFormat::R5G6B5_UINT16;
- break;
- case 4:
- default:
- format = SurfaceFormat::R8G8B8A8;
- break;
- }
- RefPtr<DataSourceSurface> surf =
- Factory::CreateWrappingDataSourceSurface((uint8_t*)bytes, strideBytes,
- IntSize(width, height),
- format);
- gfxUtils::DumpAsDataURI(surf);
- }
- }
- static uint8_t PremultiplyValue(uint8_t a, uint8_t v) {
- return gfxUtils::sPremultiplyTable[a*256+v];
- }
- static uint8_t UnpremultiplyValue(uint8_t a, uint8_t v) {
- return gfxUtils::sUnpremultiplyTable[a*256+v];
- }
- static void
- PremultiplyData(const uint8_t* srcData,
- size_t srcStride, // row-to-row stride in bytes
- uint8_t* destData,
- size_t destStride, // row-to-row stride in bytes
- size_t pixelWidth,
- size_t rowCount)
- {
- MOZ_ASSERT(srcData && destData);
- for (size_t y = 0; y < rowCount; ++y) {
- const uint8_t* src = srcData + y * srcStride;
- uint8_t* dest = destData + y * destStride;
- for (size_t x = 0; x < pixelWidth; ++x) {
- #ifdef IS_LITTLE_ENDIAN
- uint8_t b = *src++;
- uint8_t g = *src++;
- uint8_t r = *src++;
- uint8_t a = *src++;
- *dest++ = PremultiplyValue(a, b);
- *dest++ = PremultiplyValue(a, g);
- *dest++ = PremultiplyValue(a, r);
- *dest++ = a;
- #else
- uint8_t a = *src++;
- uint8_t r = *src++;
- uint8_t g = *src++;
- uint8_t b = *src++;
- *dest++ = a;
- *dest++ = PremultiplyValue(a, r);
- *dest++ = PremultiplyValue(a, g);
- *dest++ = PremultiplyValue(a, b);
- #endif
- }
- }
- }
- static void
- UnpremultiplyData(const uint8_t* srcData,
- size_t srcStride, // row-to-row stride in bytes
- uint8_t* destData,
- size_t destStride, // row-to-row stride in bytes
- size_t pixelWidth,
- size_t rowCount)
- {
- MOZ_ASSERT(srcData && destData);
- for (size_t y = 0; y < rowCount; ++y) {
- const uint8_t* src = srcData + y * srcStride;
- uint8_t* dest = destData + y * destStride;
- for (size_t x = 0; x < pixelWidth; ++x) {
- #ifdef IS_LITTLE_ENDIAN
- uint8_t b = *src++;
- uint8_t g = *src++;
- uint8_t r = *src++;
- uint8_t a = *src++;
- *dest++ = UnpremultiplyValue(a, b);
- *dest++ = UnpremultiplyValue(a, g);
- *dest++ = UnpremultiplyValue(a, r);
- *dest++ = a;
- #else
- uint8_t a = *src++;
- uint8_t r = *src++;
- uint8_t g = *src++;
- uint8_t b = *src++;
- *dest++ = a;
- *dest++ = UnpremultiplyValue(a, r);
- *dest++ = UnpremultiplyValue(a, g);
- *dest++ = UnpremultiplyValue(a, b);
- #endif
- }
- }
- }
- static bool
- MapSrcDest(DataSourceSurface* srcSurf,
- DataSourceSurface* destSurf,
- DataSourceSurface::MappedSurface* out_srcMap,
- DataSourceSurface::MappedSurface* out_destMap)
- {
- MOZ_ASSERT(srcSurf && destSurf);
- MOZ_ASSERT(out_srcMap && out_destMap);
- if (srcSurf->GetFormat() != SurfaceFormat::B8G8R8A8 ||
- destSurf->GetFormat() != SurfaceFormat::B8G8R8A8)
- {
- MOZ_ASSERT(false, "Only operate on BGRA8 surfs.");
- return false;
- }
- if (srcSurf->GetSize().width != destSurf->GetSize().width ||
- srcSurf->GetSize().height != destSurf->GetSize().height)
- {
- MOZ_ASSERT(false, "Width and height must match.");
- return false;
- }
- if (srcSurf == destSurf) {
- DataSourceSurface::MappedSurface map;
- if (!srcSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
- NS_WARNING("Couldn't Map srcSurf/destSurf.");
- return false;
- }
- *out_srcMap = map;
- *out_destMap = map;
- return true;
- }
- // Map src for reading.
- DataSourceSurface::MappedSurface srcMap;
- if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
- NS_WARNING("Couldn't Map srcSurf.");
- return false;
- }
- // Map dest for writing.
- DataSourceSurface::MappedSurface destMap;
- if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
- NS_WARNING("Couldn't Map aDest.");
- srcSurf->Unmap();
- return false;
- }
- *out_srcMap = srcMap;
- *out_destMap = destMap;
- return true;
- }
- static void
- UnmapSrcDest(DataSourceSurface* srcSurf,
- DataSourceSurface* destSurf)
- {
- if (srcSurf == destSurf) {
- srcSurf->Unmap();
- } else {
- srcSurf->Unmap();
- destSurf->Unmap();
- }
- }
- bool
- gfxUtils::PremultiplyDataSurface(DataSourceSurface* srcSurf,
- DataSourceSurface* destSurf)
- {
- MOZ_ASSERT(srcSurf && destSurf);
- DataSourceSurface::MappedSurface srcMap;
- DataSourceSurface::MappedSurface destMap;
- if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
- return false;
- PremultiplyData(srcMap.mData, srcMap.mStride,
- destMap.mData, destMap.mStride,
- srcSurf->GetSize().width,
- srcSurf->GetSize().height);
- UnmapSrcDest(srcSurf, destSurf);
- return true;
- }
- bool
- gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* srcSurf,
- DataSourceSurface* destSurf)
- {
- MOZ_ASSERT(srcSurf && destSurf);
- DataSourceSurface::MappedSurface srcMap;
- DataSourceSurface::MappedSurface destMap;
- if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
- return false;
- UnpremultiplyData(srcMap.mData, srcMap.mStride,
- destMap.mData, destMap.mStride,
- srcSurf->GetSize().width,
- srcSurf->GetSize().height);
- UnmapSrcDest(srcSurf, destSurf);
- return true;
- }
- static bool
- MapSrcAndCreateMappedDest(DataSourceSurface* srcSurf,
- RefPtr<DataSourceSurface>* out_destSurf,
- DataSourceSurface::MappedSurface* out_srcMap,
- DataSourceSurface::MappedSurface* out_destMap)
- {
- MOZ_ASSERT(srcSurf);
- MOZ_ASSERT(out_destSurf && out_srcMap && out_destMap);
- if (srcSurf->GetFormat() != SurfaceFormat::B8G8R8A8) {
- MOZ_ASSERT(false, "Only operate on BGRA8.");
- return false;
- }
- // Ok, map source for reading.
- DataSourceSurface::MappedSurface srcMap;
- if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
- MOZ_ASSERT(false, "Couldn't Map srcSurf.");
- return false;
- }
- // Make our dest surface based on the src.
- RefPtr<DataSourceSurface> destSurf =
- Factory::CreateDataSourceSurfaceWithStride(srcSurf->GetSize(),
- srcSurf->GetFormat(),
- srcMap.mStride);
- if (NS_WARN_IF(!destSurf)) {
- return false;
- }
- DataSourceSurface::MappedSurface destMap;
- if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
- MOZ_ASSERT(false, "Couldn't Map destSurf.");
- srcSurf->Unmap();
- return false;
- }
- *out_destSurf = destSurf;
- *out_srcMap = srcMap;
- *out_destMap = destMap;
- return true;
- }
- already_AddRefed<DataSourceSurface>
- gfxUtils::CreatePremultipliedDataSurface(DataSourceSurface* srcSurf)
- {
- RefPtr<DataSourceSurface> destSurf;
- DataSourceSurface::MappedSurface srcMap;
- DataSourceSurface::MappedSurface destMap;
- if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
- MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
- RefPtr<DataSourceSurface> surface(srcSurf);
- return surface.forget();
- }
- PremultiplyData(srcMap.mData, srcMap.mStride,
- destMap.mData, destMap.mStride,
- srcSurf->GetSize().width,
- srcSurf->GetSize().height);
- UnmapSrcDest(srcSurf, destSurf);
- return destSurf.forget();
- }
- already_AddRefed<DataSourceSurface>
- gfxUtils::CreateUnpremultipliedDataSurface(DataSourceSurface* srcSurf)
- {
- RefPtr<DataSourceSurface> destSurf;
- DataSourceSurface::MappedSurface srcMap;
- DataSourceSurface::MappedSurface destMap;
- if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
- MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
- RefPtr<DataSourceSurface> surface(srcSurf);
- return surface.forget();
- }
- UnpremultiplyData(srcMap.mData, srcMap.mStride,
- destMap.mData, destMap.mStride,
- srcSurf->GetSize().width,
- srcSurf->GetSize().height);
- UnmapSrcDest(srcSurf, destSurf);
- return destSurf.forget();
- }
- void
- gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength)
- {
- MOZ_ASSERT((aLength % 4) == 0, "Loop below will pass srcEnd!");
- libyuv::ABGRToARGB(aData, aLength, aData, aLength, aLength / 4, 1);
- }
- /**
- * This returns the fastest operator to use for solid surfaces which have no
- * alpha channel or their alpha channel is uniformly opaque.
- * This differs per render mode.
- */
- static CompositionOp
- OptimalFillOp()
- {
- #ifdef XP_WIN
- if (gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend()) {
- // D2D -really- hates operator source.
- return CompositionOp::OP_OVER;
- }
- #endif
- return CompositionOp::OP_SOURCE;
- }
- // EXTEND_PAD won't help us here; we have to create a temporary surface to hold
- // the subimage of pixels we're allowed to sample.
- static already_AddRefed<gfxDrawable>
- CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
- gfxContext* aContext,
- const ImageRegion& aRegion,
- const SurfaceFormat aFormat)
- {
- PROFILER_LABEL("gfxUtils", "CreateSamplingRestricedDrawable",
- js::ProfileEntry::Category::GRAPHICS);
- DrawTarget* destDrawTarget = aContext->GetDrawTarget();
- if (destDrawTarget->GetBackendType() == BackendType::DIRECT2D1_1) {
- return nullptr;
- }
- gfxRect clipExtents = aContext->GetClipExtents();
- // Inflate by one pixel because bilinear filtering will sample at most
- // one pixel beyond the computed image pixel coordinate.
- clipExtents.Inflate(1.0);
- gfxRect needed = aRegion.IntersectAndRestrict(clipExtents);
- needed.RoundOut();
- // if 'needed' is empty, nothing will be drawn since aFill
- // must be entirely outside the clip region, so it doesn't
- // matter what we do here, but we should avoid trying to
- // create a zero-size surface.
- if (needed.IsEmpty())
- return nullptr;
- IntSize size(int32_t(needed.Width()), int32_t(needed.Height()));
- RefPtr<DrawTarget> target =
- gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, aFormat);
- if (!target || !target->IsValid()) {
- return nullptr;
- }
- RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(target);
- MOZ_ASSERT(tmpCtx); // already checked the target above
- tmpCtx->SetOp(OptimalFillOp());
- aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), ExtendMode::REPEAT,
- SamplingFilter::LINEAR,
- 1.0, gfxMatrix::Translation(needed.TopLeft()));
- RefPtr<SourceSurface> surface = target->Snapshot();
- RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size, gfxMatrix::Translation(-needed.TopLeft()));
- return drawable.forget();
- }
- static SamplingFilter ReduceResamplingFilter(SamplingFilter aSamplingFilter,
- int aImgWidth, int aImgHeight,
- int aSourceWidth, int aSourceHeight)
- {
- // Just pass the filter through unchanged
- return aSamplingFilter;
- }
- #ifdef MOZ_WIDGET_COCOA
- // Only prescale a temporary surface if we're going to repeat it often.
- // Scaling is expensive on OS X and without prescaling, we'd scale
- // every tile of the repeated rect. However, using a temp surface also potentially uses
- // more memory if the scaled image is large. So only prescale on a temp
- // surface if we know we're going to repeat the image in either the X or Y axis
- // multiple times.
- static bool
- ShouldUseTempSurface(Rect aImageRect, Rect aNeededRect)
- {
- int repeatX = aNeededRect.width / aImageRect.width;
- int repeatY = aNeededRect.height / aImageRect.height;
- return (repeatX >= 5) || (repeatY >= 5);
- }
- static bool
- PrescaleAndTileDrawable(gfxDrawable* aDrawable,
- gfxContext* aContext,
- const ImageRegion& aRegion,
- Rect aImageRect,
- const SamplingFilter aSamplingFilter,
- const SurfaceFormat aFormat,
- gfxFloat aOpacity,
- ExtendMode aExtendMode)
- {
- gfxSize scaleFactor = aContext->CurrentMatrix().ScaleFactors(true);
- gfxMatrix scaleMatrix = gfxMatrix::Scaling(scaleFactor.width, scaleFactor.height);
- const float fuzzFactor = 0.01;
- // If we aren't scaling or translating, don't go down this path
- if ((FuzzyEqual(scaleFactor.width, 1.0, fuzzFactor) &&
- FuzzyEqual(scaleFactor.width, 1.0, fuzzFactor)) ||
- aContext->CurrentMatrix().HasNonAxisAlignedTransform()) {
- return false;
- }
- gfxRect clipExtents = aContext->GetClipExtents();
- // Inflate by one pixel because bilinear filtering will sample at most
- // one pixel beyond the computed image pixel coordinate.
- clipExtents.Inflate(1.0);
- gfxRect needed = aRegion.IntersectAndRestrict(clipExtents);
- Rect scaledNeededRect = ToMatrix(scaleMatrix).TransformBounds(ToRect(needed));
- scaledNeededRect.RoundOut();
- if (scaledNeededRect.IsEmpty()) {
- return false;
- }
- Rect scaledImageRect = ToMatrix(scaleMatrix).TransformBounds(aImageRect);
- if (!ShouldUseTempSurface(scaledImageRect, scaledNeededRect)) {
- return false;
- }
- IntSize scaledImageSize((int32_t)scaledImageRect.width,
- (int32_t)scaledImageRect.height);
- if (scaledImageSize.width != scaledImageRect.width ||
- scaledImageSize.height != scaledImageRect.height) {
- // If the scaled image isn't pixel aligned, we'll get artifacts
- // so we have to take the slow path.
- return false;
- }
- RefPtr<DrawTarget> scaledDT =
- gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(scaledImageSize, aFormat);
- if (!scaledDT || !scaledDT->IsValid()) {
- return false;
- }
- RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(scaledDT);
- MOZ_ASSERT(tmpCtx); // already checked the target above
- scaledDT->SetTransform(ToMatrix(scaleMatrix));
- gfxRect gfxImageRect(aImageRect.x, aImageRect.y, aImageRect.width, aImageRect.height);
- // Since this is just the scaled image, we don't want to repeat anything yet.
- aDrawable->Draw(tmpCtx, gfxImageRect, ExtendMode::CLAMP, aSamplingFilter, 1.0, gfxMatrix());
- RefPtr<SourceSurface> scaledImage = scaledDT->Snapshot();
- {
- gfxContextMatrixAutoSaveRestore autoSR(aContext);
- Matrix withoutScale = ToMatrix(aContext->CurrentMatrix());
- DrawTarget* destDrawTarget = aContext->GetDrawTarget();
- // The translation still is in scaled units
- withoutScale.PreScale(1.0 / scaleFactor.width, 1.0 / scaleFactor.height);
- aContext->SetMatrix(ThebesMatrix(withoutScale));
- DrawOptions drawOptions(aOpacity, aContext->CurrentOp(),
- aContext->CurrentAntialiasMode());
- SurfacePattern scaledImagePattern(scaledImage, aExtendMode,
- Matrix(), aSamplingFilter);
- destDrawTarget->FillRect(scaledNeededRect, scaledImagePattern, drawOptions);
- }
- return true;
- }
- #endif // MOZ_WIDGET_COCOA
- /* static */ void
- gfxUtils::DrawPixelSnapped(gfxContext* aContext,
- gfxDrawable* aDrawable,
- const gfxSize& aImageSize,
- const ImageRegion& aRegion,
- const SurfaceFormat aFormat,
- SamplingFilter aSamplingFilter,
- uint32_t aImageFlags,
- gfxFloat aOpacity)
- {
- PROFILER_LABEL("gfxUtils", "DrawPixelSnapped",
- js::ProfileEntry::Category::GRAPHICS);
- gfxRect imageRect(gfxPoint(0, 0), aImageSize);
- gfxRect region(aRegion.Rect());
- ExtendMode extendMode = aRegion.GetExtendMode();
- RefPtr<gfxDrawable> drawable = aDrawable;
- aSamplingFilter =
- ReduceResamplingFilter(aSamplingFilter,
- imageRect.Width(), imageRect.Height(),
- region.Width(), region.Height());
- // OK now, the hard part left is to account for the subimage sampling
- // restriction. If all the transforms involved are just integer
- // translations, then we assume no resampling will occur so there's
- // nothing to do.
- // XXX if only we had source-clipping in cairo!
- if (aContext->CurrentMatrix().HasNonIntegerTranslation()) {
- if ((extendMode != ExtendMode::CLAMP) || !aRegion.RestrictionContains(imageRect)) {
- if (drawable->DrawWithSamplingRect(aContext->GetDrawTarget(),
- aContext->CurrentOp(),
- aContext->CurrentAntialiasMode(),
- aRegion.Rect(),
- aRegion.Restriction(),
- extendMode, aSamplingFilter,
- aOpacity)) {
- return;
- }
- #ifdef MOZ_WIDGET_COCOA
- if (PrescaleAndTileDrawable(aDrawable, aContext, aRegion,
- ToRect(imageRect), aSamplingFilter,
- aFormat, aOpacity, extendMode)) {
- return;
- }
- #endif
- RefPtr<gfxDrawable> restrictedDrawable =
- CreateSamplingRestrictedDrawable(aDrawable, aContext,
- aRegion, aFormat);
- if (restrictedDrawable) {
- drawable.swap(restrictedDrawable);
- // We no longer need to tile: Either we never needed to, or we already
- // filled a surface with the tiled pattern; this surface can now be
- // drawn without tiling.
- extendMode = ExtendMode::CLAMP;
- }
- }
- }
- drawable->Draw(aContext, aRegion.Rect(), extendMode, aSamplingFilter,
- aOpacity, gfxMatrix());
- }
- /* static */ int
- gfxUtils::ImageFormatToDepth(gfxImageFormat aFormat)
- {
- switch (aFormat) {
- case SurfaceFormat::A8R8G8B8_UINT32:
- return 32;
- case SurfaceFormat::X8R8G8B8_UINT32:
- return 24;
- case SurfaceFormat::R5G6B5_UINT16:
- return 16;
- default:
- break;
- }
- return 0;
- }
- /*static*/ void
- gfxUtils::ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion)
- {
- aContext->NewPath();
- for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
- const IntRect& r = iter.Get();
- aContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
- }
- aContext->Clip();
- }
- /*static*/ void
- gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion)
- {
- uint32_t numRects = aRegion.GetNumRects();
- // If there is only one rect, then the region bounds are equivalent to the
- // contents. So just use push a single clip rect with the bounds.
- if (numRects == 1) {
- aTarget->PushClipRect(Rect(aRegion.GetBounds()));
- return;
- }
- // Check if the target's transform will preserve axis-alignment and
- // pixel-alignment for each rect. For now, just handle the common case
- // of integer translations.
- Matrix transform = aTarget->GetTransform();
- if (transform.IsIntegerTranslation()) {
- IntPoint translation = RoundedToInt(transform.GetTranslation());
- AutoTArray<IntRect, 16> rects;
- rects.SetLength(numRects);
- uint32_t i = 0;
- // Build the list of transformed rects by adding in the translation.
- for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
- IntRect rect = iter.Get();
- rect.MoveBy(translation);
- rects[i++] = rect;
- }
- aTarget->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
- } else {
- // The transform does not produce axis-aligned rects or a rect was not
- // pixel-aligned. So just build a path with all the rects and clip to it
- // instead.
- RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
- for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
- AppendRectToPath(pathBuilder, Rect(iter.Get()));
- }
- RefPtr<Path> path = pathBuilder->Finish();
- aTarget->PushClip(path);
- }
- }
- /*static*/ gfxFloat
- gfxUtils::ClampToScaleFactor(gfxFloat aVal)
- {
- // Arbitary scale factor limitation. We can increase this
- // for better scaling performance at the cost of worse
- // quality.
- static const gfxFloat kScaleResolution = 2;
- // Negative scaling is just a flip and irrelevant to
- // our resolution calculation.
- if (aVal < 0.0) {
- aVal = -aVal;
- }
- bool inverse = false;
- if (aVal < 1.0) {
- inverse = true;
- aVal = 1 / aVal;
- }
- gfxFloat power = log(aVal)/log(kScaleResolution);
- // If power is within 1e-5 of an integer, round to nearest to
- // prevent floating point errors, otherwise round up to the
- // next integer value.
- if (fabs(power - NS_round(power)) < 1e-5) {
- power = NS_round(power);
- } else if (inverse) {
- power = floor(power);
- } else {
- power = ceil(power);
- }
- gfxFloat scale = pow(kScaleResolution, power);
- if (inverse) {
- scale = 1 / scale;
- }
- return scale;
- }
- gfxMatrix
- gfxUtils::TransformRectToRect(const gfxRect& aFrom, const gfxPoint& aToTopLeft,
- const gfxPoint& aToTopRight, const gfxPoint& aToBottomRight)
- {
- gfxMatrix m;
- if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) {
- // Not a rotation, so xy and yx are zero
- m._21 = m._12 = 0.0;
- m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.width;
- m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.height;
- m._31 = aToTopLeft.x - m._11*aFrom.x;
- m._32 = aToTopLeft.y - m._22*aFrom.y;
- } else {
- NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x,
- "Destination rectangle not axis-aligned");
- m._11 = m._22 = 0.0;
- m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.height;
- m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.width;
- m._31 = aToTopLeft.x - m._21*aFrom.y;
- m._32 = aToTopLeft.y - m._12*aFrom.x;
- }
- return m;
- }
- Matrix
- gfxUtils::TransformRectToRect(const gfxRect& aFrom, const IntPoint& aToTopLeft,
- const IntPoint& aToTopRight, const IntPoint& aToBottomRight)
- {
- Matrix m;
- if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) {
- // Not a rotation, so xy and yx are zero
- m._12 = m._21 = 0.0;
- m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.width;
- m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.height;
- m._31 = aToTopLeft.x - m._11*aFrom.x;
- m._32 = aToTopLeft.y - m._22*aFrom.y;
- } else {
- NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x,
- "Destination rectangle not axis-aligned");
- m._11 = m._22 = 0.0;
- m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.height;
- m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.width;
- m._31 = aToTopLeft.x - m._21*aFrom.y;
- m._32 = aToTopLeft.y - m._12*aFrom.x;
- }
- return m;
- }
- /* This function is sort of shitty. We truncate doubles
- * to ints then convert those ints back to doubles to make sure that
- * they equal the doubles that we got in. */
- bool
- gfxUtils::GfxRectToIntRect(const gfxRect& aIn, IntRect* aOut)
- {
- *aOut = IntRect(int32_t(aIn.X()), int32_t(aIn.Y()),
- int32_t(aIn.Width()), int32_t(aIn.Height()));
- return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height).IsEqualEdges(aIn);
- }
- /* static */ void gfxUtils::ClearThebesSurface(gfxASurface* aSurface)
- {
- if (aSurface->CairoStatus()) {
- return;
- }
- cairo_surface_t* surf = aSurface->CairoSurface();
- if (cairo_surface_status(surf)) {
- return;
- }
- cairo_t* ctx = cairo_create(surf);
- cairo_set_source_rgba(ctx, 0.0, 0.0, 0.0, 0.0);
- cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
- IntRect bounds(nsIntPoint(0, 0), aSurface->GetSize());
- cairo_rectangle(ctx, bounds.x, bounds.y, bounds.width, bounds.height);
- cairo_fill(ctx);
- cairo_destroy(ctx);
- }
- /* static */ already_AddRefed<DataSourceSurface>
- gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
- SurfaceFormat aFormat)
- {
- MOZ_ASSERT(aFormat != aSurface->GetFormat(),
- "Unnecessary - and very expersive - surface format conversion");
- Rect bounds(0, 0, aSurface->GetSize().width, aSurface->GetSize().height);
- if (aSurface->GetType() != SurfaceType::DATA) {
- // If the surface is NOT of type DATA then its data is not mapped into main
- // memory. Format conversion is probably faster on the GPU, and by doing it
- // there we can avoid any expensive uploads/readbacks except for (possibly)
- // a single readback due to the unavoidable GetDataSurface() call. Using
- // CreateOffscreenContentDrawTarget ensures the conversion happens on the
- // GPU.
- RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
- CreateOffscreenContentDrawTarget(aSurface->GetSize(), aFormat);
- if (!dt) {
- gfxWarning() << "gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat failed in CreateOffscreenContentDrawTarget";
- return nullptr;
- }
- // Using DrawSurface() here rather than CopySurface() because CopySurface
- // is optimized for memcpy and therefore isn't good for format conversion.
- // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
- // generally more optimized.
- dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
- DrawOptions(1.0f, CompositionOp::OP_OVER));
- RefPtr<SourceSurface> surface = dt->Snapshot();
- return surface->GetDataSurface();
- }
- // If the surface IS of type DATA then it may or may not be in main memory
- // depending on whether or not it has been mapped yet. We have no way of
- // knowing, so we can't be sure if it's best to create a data wrapping
- // DrawTarget for the conversion or an offscreen content DrawTarget. We could
- // guess it's not mapped and create an offscreen content DrawTarget, but if
- // it is then we'll end up uploading the surface data, and most likely the
- // caller is going to be accessing the resulting surface data, resulting in a
- // readback (both very expensive operations). Alternatively we could guess
- // the data is mapped and create a data wrapping DrawTarget and, if the
- // surface is not in main memory, then we will incure a readback. The latter
- // of these two "wrong choices" is the least costly (a readback, vs an
- // upload and a readback), and more than likely the DATA surface that we've
- // been passed actually IS in main memory anyway. For these reasons it's most
- // likely best to create a data wrapping DrawTarget here to do the format
- // conversion.
- RefPtr<DataSourceSurface> dataSurface =
- Factory::CreateDataSourceSurface(aSurface->GetSize(), aFormat);
- DataSourceSurface::MappedSurface map;
- if (!dataSurface ||
- !dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
- return nullptr;
- }
- RefPtr<DrawTarget> dt =
- Factory::CreateDrawTargetForData(BackendType::CAIRO,
- map.mData,
- dataSurface->GetSize(),
- map.mStride,
- aFormat);
- if (!dt) {
- dataSurface->Unmap();
- return nullptr;
- }
- // Using DrawSurface() here rather than CopySurface() because CopySurface
- // is optimized for memcpy and therefore isn't good for format conversion.
- // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
- // generally more optimized.
- dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
- DrawOptions(1.0f, CompositionOp::OP_OVER));
- dataSurface->Unmap();
- return dataSurface.forget();
- }
- const uint32_t gfxUtils::sNumFrameColors = 8;
- /* static */ const gfx::Color&
- gfxUtils::GetColorForFrameNumber(uint64_t aFrameNumber)
- {
- static bool initialized = false;
- static gfx::Color colors[sNumFrameColors];
- if (!initialized) {
- uint32_t i = 0;
- colors[i++] = gfx::Color::FromABGR(0xffff0000);
- colors[i++] = gfx::Color::FromABGR(0xffcc00ff);
- colors[i++] = gfx::Color::FromABGR(0xff0066cc);
- colors[i++] = gfx::Color::FromABGR(0xff00ff00);
- colors[i++] = gfx::Color::FromABGR(0xff33ffff);
- colors[i++] = gfx::Color::FromABGR(0xffff0099);
- colors[i++] = gfx::Color::FromABGR(0xff0000ff);
- colors[i++] = gfx::Color::FromABGR(0xff999999);
- MOZ_ASSERT(i == sNumFrameColors);
- initialized = true;
- }
- return colors[aFrameNumber % sNumFrameColors];
- }
- static nsresult
- EncodeSourceSurfaceInternal(SourceSurface* aSurface,
- const nsACString& aMimeType,
- const nsAString& aOutputOptions,
- gfxUtils::BinaryOrData aBinaryOrData,
- FILE* aFile,
- nsCString* aStrOut)
- {
- MOZ_ASSERT(aBinaryOrData == gfxUtils::eDataURIEncode || aFile || aStrOut,
- "Copying binary encoding to clipboard not currently supported");
- const IntSize size = aSurface->GetSize();
- if (size.IsEmpty()) {
- return NS_ERROR_INVALID_ARG;
- }
- const Size floatSize(size.width, size.height);
- RefPtr<DataSourceSurface> dataSurface;
- if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
- // FIXME bug 995807 (B8G8R8X8), bug 831898 (R5G6B5)
- dataSurface =
- gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(aSurface,
- SurfaceFormat::B8G8R8A8);
- } else {
- dataSurface = aSurface->GetDataSurface();
- }
- if (!dataSurface) {
- return NS_ERROR_FAILURE;
- }
- DataSourceSurface::MappedSurface map;
- if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
- return NS_ERROR_FAILURE;
- }
- nsAutoCString encoderCID(
- NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
- nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
- if (!encoder) {
- #ifdef DEBUG
- int32_t w = std::min(size.width, 8);
- int32_t h = std::min(size.height, 8);
- printf("Could not create encoder. Top-left %dx%d pixels contain:\n", w, h);
- for (int32_t y = 0; y < h; ++y) {
- for (int32_t x = 0; x < w; ++x) {
- printf("%x ", reinterpret_cast<uint32_t*>(map.mData)[y*map.mStride+x]);
- }
- }
- #endif
- dataSurface->Unmap();
- return NS_ERROR_FAILURE;
- }
- nsresult rv = encoder->InitFromData(map.mData,
- BufferSizeFromStrideAndHeight(map.mStride, size.height),
- size.width,
- size.height,
- map.mStride,
- imgIEncoder::INPUT_FORMAT_HOSTARGB,
- aOutputOptions);
- dataSurface->Unmap();
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIInputStream> imgStream;
- CallQueryInterface(encoder.get(), getter_AddRefs(imgStream));
- if (!imgStream) {
- return NS_ERROR_FAILURE;
- }
- uint64_t bufSize64;
- rv = imgStream->Available(&bufSize64);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(bufSize64 < UINT32_MAX - 16, NS_ERROR_FAILURE);
- uint32_t bufSize = (uint32_t)bufSize64;
- // ...leave a little extra room so we can call read again and make sure we
- // got everything. 16 bytes for better padding (maybe)
- bufSize += 16;
- uint32_t imgSize = 0;
- Vector<char> imgData;
- if (!imgData.initCapacity(bufSize)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- uint32_t numReadThisTime = 0;
- while ((rv = imgStream->Read(imgData.begin() + imgSize,
- bufSize - imgSize,
- &numReadThisTime)) == NS_OK && numReadThisTime > 0)
- {
- // Update the length of the vector without overwriting the new data.
- if (!imgData.growByUninitialized(numReadThisTime)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- imgSize += numReadThisTime;
- if (imgSize == bufSize) {
- // need a bigger buffer, just double
- bufSize *= 2;
- if (!imgData.resizeUninitialized(bufSize)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- }
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(!imgData.empty(), NS_ERROR_FAILURE);
- if (aBinaryOrData == gfxUtils::eBinaryEncode) {
- if (aFile) {
- fwrite(imgData.begin(), 1, imgSize, aFile);
- }
- return NS_OK;
- }
- // base 64, result will be null-terminated
- nsCString encodedImg;
- rv = Base64Encode(Substring(imgData.begin(), imgSize), encodedImg);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCString string("data:");
- string.Append(aMimeType);
- string.Append(";base64,");
- string.Append(encodedImg);
- if (aFile) {
- #ifdef ANDROID
- if (aFile == stdout || aFile == stderr) {
- // ADB logcat cuts off long strings so we will break it down
- const char* cStr = string.BeginReading();
- size_t len = strlen(cStr);
- while (true) {
- printf_stderr("IMG: %.140s\n", cStr);
- if (len <= 140)
- break;
- len -= 140;
- cStr += 140;
- }
- }
- #endif
- fprintf(aFile, "%s", string.BeginReading());
- } else if (aStrOut) {
- *aStrOut = string;
- } else {
- nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
- if (clipboard) {
- clipboard->CopyString(NS_ConvertASCIItoUTF16(string));
- }
- }
- return NS_OK;
- }
- static nsCString
- EncodeSourceSurfaceAsPNGURI(SourceSurface* aSurface)
- {
- nsCString string;
- EncodeSourceSurfaceInternal(aSurface, NS_LITERAL_CSTRING("image/png"),
- EmptyString(), gfxUtils::eDataURIEncode,
- nullptr, &string);
- return string;
- }
- /* static */ nsresult
- gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
- const nsACString& aMimeType,
- const nsAString& aOutputOptions,
- BinaryOrData aBinaryOrData,
- FILE* aFile)
- {
- return EncodeSourceSurfaceInternal(aSurface, aMimeType, aOutputOptions,
- aBinaryOrData, aFile, nullptr);
- }
- /* From Rec601:
- [R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
- [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
- [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
- For [0,1] instead of [0,255], and to 5 places:
- [R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
- [G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
- [B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
- From Rec709:
- [R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
- [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
- [B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
- For [0,1] instead of [0,255], and to 5 places:
- [R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
- [G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
- [B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
- */
- /* static */ float*
- gfxUtils::Get4x3YuvColorMatrix(YUVColorSpace aYUVColorSpace)
- {
- static const float yuv_to_rgb_rec601[12] = { 1.16438f, 0.0f, 1.59603f, 0.0f,
- 1.16438f, -0.39176f, -0.81297f, 0.0f,
- 1.16438f, 2.01723f, 0.0f, 0.0f,
- };
- static const float yuv_to_rgb_rec709[12] = { 1.16438f, 0.0f, 1.79274f, 0.0f,
- 1.16438f, -0.21325f, -0.53291f, 0.0f,
- 1.16438f, 2.11240f, 0.0f, 0.0f,
- };
- if (aYUVColorSpace == YUVColorSpace::BT709) {
- return const_cast<float*>(yuv_to_rgb_rec709);
- } else {
- return const_cast<float*>(yuv_to_rgb_rec601);
- }
- }
- /* static */ float*
- gfxUtils::Get3x3YuvColorMatrix(YUVColorSpace aYUVColorSpace)
- {
- static const float yuv_to_rgb_rec601[9] = {
- 1.16438f, 1.16438f, 1.16438f, 0.0f, -0.39176f, 2.01723f, 1.59603f, -0.81297f, 0.0f,
- };
- static const float yuv_to_rgb_rec709[9] = {
- 1.16438f, 1.16438f, 1.16438f, 0.0f, -0.21325f, 2.11240f, 1.79274f, -0.53291f, 0.0f,
- };
- if (aYUVColorSpace == YUVColorSpace::BT709) {
- return const_cast<float*>(yuv_to_rgb_rec709);
- } else {
- return const_cast<float*>(yuv_to_rgb_rec601);
- }
- }
- /* static */ void
- gfxUtils::WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile)
- {
- WriteAsPNG(aSurface, NS_ConvertUTF16toUTF8(aFile).get());
- }
- /* static */ void
- gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile)
- {
- FILE* file = fopen(aFile, "wb");
- if (!file) {
- // Maybe the directory doesn't exist; try creating it, then fopen again.
- nsresult rv = NS_ERROR_FAILURE;
- nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
- if (comFile) {
- NS_ConvertUTF8toUTF16 utf16path((nsDependentCString(aFile)));
- rv = comFile->InitWithPath(utf16path);
- if (NS_SUCCEEDED(rv)) {
- nsCOMPtr<nsIFile> dirPath;
- comFile->GetParent(getter_AddRefs(dirPath));
- if (dirPath) {
- rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
- if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
- file = fopen(aFile, "wb");
- }
- }
- }
- }
- if (!file) {
- NS_WARNING("Failed to open file to create PNG!");
- return;
- }
- }
- EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
- EmptyString(), eBinaryEncode, file);
- fclose(file);
- }
- /* static */ void
- gfxUtils::WriteAsPNG(DrawTarget* aDT, const nsAString& aFile)
- {
- WriteAsPNG(aDT, NS_ConvertUTF16toUTF8(aFile).get());
- }
- /* static */ void
- gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile)
- {
- RefPtr<SourceSurface> surface = aDT->Snapshot();
- if (surface) {
- WriteAsPNG(surface, aFile);
- } else {
- NS_WARNING("Failed to get surface!");
- }
- }
- /* static */ void
- gfxUtils::WriteAsPNG(nsIPresShell* aShell, const char* aFile)
- {
- int32_t width = 1000, height = 1000;
- nsRect r(0, 0, aShell->GetPresContext()->DevPixelsToAppUnits(width),
- aShell->GetPresContext()->DevPixelsToAppUnits(height));
- RefPtr<mozilla::gfx::DrawTarget> dt = gfxPlatform::GetPlatform()->
- CreateOffscreenContentDrawTarget(IntSize(width, height),
- SurfaceFormat::B8G8R8A8);
- NS_ENSURE_TRUE(dt && dt->IsValid(), /*void*/);
- RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
- MOZ_ASSERT(context); // already checked the draw target above
- aShell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
- WriteAsPNG(dt.get(), aFile);
- }
- /* static */ void
- gfxUtils::DumpAsDataURI(SourceSurface* aSurface, FILE* aFile)
- {
- EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
- EmptyString(), eDataURIEncode, aFile);
- }
- /* static */ nsCString
- gfxUtils::GetAsDataURI(SourceSurface* aSurface)
- {
- return EncodeSourceSurfaceAsPNGURI(aSurface);
- }
- /* static */ void
- gfxUtils::DumpAsDataURI(DrawTarget* aDT, FILE* aFile)
- {
- RefPtr<SourceSurface> surface = aDT->Snapshot();
- if (surface) {
- DumpAsDataURI(surface, aFile);
- } else {
- NS_WARNING("Failed to get surface!");
- }
- }
- /* static */ nsCString
- gfxUtils::GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface)
- {
- int32_t dataSize = aSourceSurface->GetSize().height * aSourceSurface->Stride();
- auto compressedData = MakeUnique<char[]>(LZ4::maxCompressedSize(dataSize));
- if (compressedData) {
- int nDataSize = LZ4::compress((char*)aSourceSurface->GetData(),
- dataSize,
- compressedData.get());
- if (nDataSize > 0) {
- nsCString encodedImg;
- nsresult rv = Base64Encode(Substring(compressedData.get(), nDataSize), encodedImg);
- if (rv == NS_OK) {
- nsCString string("");
- string.AppendPrintf("data:image/lz4bgra;base64,%i,%i,%i,",
- aSourceSurface->GetSize().width,
- aSourceSurface->Stride(),
- aSourceSurface->GetSize().height);
- string.Append(encodedImg);
- return string;
- }
- }
- }
- return nsCString("");
- }
- /* static */ nsCString
- gfxUtils::GetAsDataURI(DrawTarget* aDT)
- {
- RefPtr<SourceSurface> surface = aDT->Snapshot();
- if (surface) {
- return EncodeSourceSurfaceAsPNGURI(surface);
- } else {
- NS_WARNING("Failed to get surface!");
- return nsCString("");
- }
- }
- /* static */ void
- gfxUtils::CopyAsDataURI(SourceSurface* aSurface)
- {
- EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
- EmptyString(), eDataURIEncode, nullptr);
- }
- /* static */ void
- gfxUtils::CopyAsDataURI(DrawTarget* aDT)
- {
- RefPtr<SourceSurface> surface = aDT->Snapshot();
- if (surface) {
- CopyAsDataURI(surface);
- } else {
- NS_WARNING("Failed to get surface!");
- }
- }
- /* static */ UniquePtr<uint8_t[]>
- gfxUtils::GetImageBuffer(gfx::DataSourceSurface* aSurface,
- bool aIsAlphaPremultiplied,
- int32_t* outFormat)
- {
- *outFormat = 0;
- DataSourceSurface::MappedSurface map;
- if (!aSurface->Map(DataSourceSurface::MapType::READ, &map))
- return nullptr;
- uint32_t bufferSize = aSurface->GetSize().width * aSurface->GetSize().height * 4;
- auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize);
- if (!imageBuffer) {
- aSurface->Unmap();
- return nullptr;
- }
- memcpy(imageBuffer.get(), map.mData, bufferSize);
- aSurface->Unmap();
- int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
- if (!aIsAlphaPremultiplied) {
- // We need to convert to INPUT_FORMAT_RGBA, otherwise
- // we are automatically considered premult, and unpremult'd.
- // Yes, it is THAT silly.
- // Except for different lossy conversions by color,
- // we could probably just change the label, and not change the data.
- gfxUtils::ConvertBGRAtoRGBA(imageBuffer.get(), bufferSize);
- format = imgIEncoder::INPUT_FORMAT_RGBA;
- }
- *outFormat = format;
- return imageBuffer;
- }
- /* static */ nsresult
- gfxUtils::GetInputStream(gfx::DataSourceSurface* aSurface,
- bool aIsAlphaPremultiplied,
- const char* aMimeType,
- const char16_t* aEncoderOptions,
- nsIInputStream** outStream)
- {
- nsCString enccid("@mozilla.org/image/encoder;2?type=");
- enccid += aMimeType;
- nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
- if (!encoder)
- return NS_ERROR_FAILURE;
- int32_t format = 0;
- UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(aSurface, aIsAlphaPremultiplied, &format);
- if (!imageBuffer)
- return NS_ERROR_FAILURE;
- return dom::ImageEncoder::GetInputStream(aSurface->GetSize().width,
- aSurface->GetSize().height,
- imageBuffer.get(), format,
- encoder, aEncoderOptions, outStream);
- }
- class GetFeatureStatusRunnable final : public dom::workers::WorkerMainThreadRunnable
- {
- public:
- GetFeatureStatusRunnable(dom::workers::WorkerPrivate* workerPrivate,
- const nsCOMPtr<nsIGfxInfo>& gfxInfo,
- int32_t feature,
- nsACString& failureId,
- int32_t* status)
- : WorkerMainThreadRunnable(workerPrivate,
- NS_LITERAL_CSTRING("GFX :: GetFeatureStatus"))
- , mGfxInfo(gfxInfo)
- , mFeature(feature)
- , mStatus(status)
- , mFailureId(failureId)
- , mNSResult(NS_OK)
- {
- }
- bool MainThreadRun() override
- {
- if (mGfxInfo) {
- mNSResult = mGfxInfo->GetFeatureStatus(mFeature, mFailureId, mStatus);
- }
- return true;
- }
- nsresult GetNSResult() const
- {
- return mNSResult;
- }
- protected:
- ~GetFeatureStatusRunnable() {}
- private:
- nsCOMPtr<nsIGfxInfo> mGfxInfo;
- int32_t mFeature;
- int32_t* mStatus;
- nsACString& mFailureId;
- nsresult mNSResult;
- };
- /* static */ nsresult
- gfxUtils::ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
- int32_t feature, nsACString& failureId,
- int32_t* status)
- {
- if (!NS_IsMainThread()) {
- dom::workers::WorkerPrivate* workerPrivate =
- dom::workers::GetCurrentThreadWorkerPrivate();
- RefPtr<GetFeatureStatusRunnable> runnable =
- new GetFeatureStatusRunnable(workerPrivate, gfxInfo, feature, failureId,
- status);
- ErrorResult rv;
- runnable->Dispatch(dom::workers::Terminating, rv);
- if (rv.Failed()) {
- // XXXbz This is totally broken, since we're supposed to just abort
- // everything up the callstack but the callers basically eat the
- // exception. Ah, well.
- return rv.StealNSResult();
- }
- return runnable->GetNSResult();
- }
- return gfxInfo->GetFeatureStatus(feature, failureId, status);
- }
- /* static */ bool
- gfxUtils::IsFeatureBlacklisted(nsCOMPtr<nsIGfxInfo> gfxInfo, int32_t feature,
- nsACString* const out_blacklistId)
- {
- if (!gfxInfo) {
- gfxInfo = services::GetGfxInfo();
- }
- int32_t status;
- if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature,
- *out_blacklistId, &status)))
- {
- out_blacklistId->AssignLiteral("");
- return true;
- }
- return status != nsIGfxInfo::FEATURE_STATUS_OK;
- }
- /* static */ bool
- gfxUtils::DumpDisplayList() {
- return gfxPrefs::LayoutDumpDisplayList() ||
- (gfxPrefs::LayoutDumpDisplayListContent() && XRE_IsContentProcess());
- }
- FILE *gfxUtils::sDumpPaintFile = stderr;
- namespace mozilla {
- namespace gfx {
- Color ToDeviceColor(Color aColor)
- {
- // aColor is pass-by-value since to get return value optimization goodness we
- // need to return the same object from all return points in this function. We
- // could declare a local Color variable and use that, but we might as well
- // just use aColor.
- if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
- qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
- if (transform) {
- gfxPlatform::TransformPixel(aColor, aColor, transform);
- // Use the original alpha to avoid unnecessary float->byte->float
- // conversion errors
- }
- }
- return aColor;
- }
- Color ToDeviceColor(nscolor aColor)
- {
- return ToDeviceColor(Color::FromABGR(aColor));
- }
- } // namespace gfx
- } // namespace mozilla
|