LCOV - code coverage report
Current view: top level - gfx/thebes - gfxUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 35 545 6.4 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "gfxUtils.h"
       7             : 
       8             : #include "cairo.h"
       9             : #include "gfxContext.h"
      10             : #include "gfxEnv.h"
      11             : #include "gfxImageSurface.h"
      12             : #include "gfxPlatform.h"
      13             : #include "gfxDrawable.h"
      14             : #include "gfxQuad.h"
      15             : #include "imgIEncoder.h"
      16             : #include "mozilla/Base64.h"
      17             : #include "mozilla/dom/ImageEncoder.h"
      18             : #include "mozilla/dom/WorkerPrivate.h"
      19             : #include "mozilla/dom/WorkerRunnable.h"
      20             : #include "mozilla/gfx/2D.h"
      21             : #include "mozilla/gfx/DataSurfaceHelpers.h"
      22             : #include "mozilla/gfx/Logging.h"
      23             : #include "mozilla/gfx/PathHelpers.h"
      24             : #include "mozilla/gfx/Swizzle.h"
      25             : #include "mozilla/gfx/gfxVars.h"
      26             : #include "mozilla/Maybe.h"
      27             : #include "mozilla/RefPtr.h"
      28             : #include "mozilla/UniquePtrExtensions.h"
      29             : #include "mozilla/Unused.h"
      30             : #include "mozilla/Vector.h"
      31             : #include "mozilla/webrender/webrender_ffi.h"
      32             : #include "nsAppRunner.h"
      33             : #include "nsComponentManagerUtils.h"
      34             : #include "nsIClipboardHelper.h"
      35             : #include "nsIFile.h"
      36             : #include "nsIGfxInfo.h"
      37             : #include "nsIPresShell.h"
      38             : #include "nsPresContext.h"
      39             : #include "nsRegion.h"
      40             : #include "nsServiceManagerUtils.h"
      41             : #include "GeckoProfiler.h"
      42             : #include "ImageContainer.h"
      43             : #include "ImageRegion.h"
      44             : #include "gfx2DGlue.h"
      45             : #include "gfxPrefs.h"
      46             : 
      47             : #ifdef XP_WIN
      48             : #include "gfxWindowsPlatform.h"
      49             : #endif
      50             : 
      51             : using namespace mozilla;
      52             : using namespace mozilla::image;
      53             : using namespace mozilla::layers;
      54             : using namespace mozilla::gfx;
      55             : 
      56             : #undef compress
      57             : #include "mozilla/Compression.h"
      58             : 
      59             : using namespace mozilla::Compression;
      60             : extern "C" {
      61             : 
      62             : /**
      63             :  * Dump a raw image to the default log.  This function is exported
      64             :  * from libxul, so it can be called from any library in addition to
      65             :  * (of course) from a debugger.
      66             :  *
      67             :  * Note: this helper currently assumes that all 2-bytepp images are
      68             :  * r5g6b5, and that all 4-bytepp images are r8g8b8a8.
      69             :  */
      70             : NS_EXPORT
      71           0 : void mozilla_dump_image(void* bytes, int width, int height, int bytepp,
      72             :                         int strideBytes)
      73             : {
      74           0 :     if (0 == strideBytes) {
      75           0 :         strideBytes = width * bytepp;
      76             :     }
      77             :     SurfaceFormat format;
      78             :     // TODO more flexible; parse string?
      79           0 :     switch (bytepp) {
      80             :     case 2:
      81             :         format = SurfaceFormat::R5G6B5_UINT16;
      82             :         break;
      83             :     case 4:
      84             :     default:
      85           0 :         format = SurfaceFormat::R8G8B8A8;
      86           0 :         break;
      87             :     }
      88             : 
      89             :     RefPtr<DataSourceSurface> surf =
      90           0 :         Factory::CreateWrappingDataSourceSurface((uint8_t*)bytes, strideBytes,
      91           0 :                                                  IntSize(width, height),
      92           0 :                                                  format);
      93           0 :     gfxUtils::DumpAsDataURI(surf);
      94           0 : }
      95             : 
      96             : }
      97             : 
      98             : static bool
      99           0 : MapSrcDest(DataSourceSurface* srcSurf,
     100             :            DataSourceSurface* destSurf,
     101             :            DataSourceSurface::MappedSurface* out_srcMap,
     102             :            DataSourceSurface::MappedSurface* out_destMap)
     103             : {
     104           0 :     MOZ_ASSERT(srcSurf && destSurf);
     105           0 :     MOZ_ASSERT(out_srcMap && out_destMap);
     106             : 
     107           0 :     if (srcSurf->GetSize() != destSurf->GetSize()) {
     108           0 :         MOZ_ASSERT(false, "Width and height must match.");
     109             :         return false;
     110             :     }
     111             : 
     112           0 :     if (srcSurf == destSurf) {
     113             :         DataSourceSurface::MappedSurface map;
     114           0 :         if (!srcSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
     115           0 :             NS_WARNING("Couldn't Map srcSurf/destSurf.");
     116           0 :             return false;
     117             :         }
     118             : 
     119           0 :         *out_srcMap = map;
     120           0 :         *out_destMap = map;
     121           0 :         return true;
     122             :     }
     123             : 
     124             :     // Map src for reading.
     125             :     DataSourceSurface::MappedSurface srcMap;
     126           0 :     if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
     127           0 :         NS_WARNING("Couldn't Map srcSurf.");
     128           0 :         return false;
     129             :     }
     130             : 
     131             :     // Map dest for writing.
     132             :     DataSourceSurface::MappedSurface destMap;
     133           0 :     if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
     134           0 :         NS_WARNING("Couldn't Map aDest.");
     135           0 :         srcSurf->Unmap();
     136           0 :         return false;
     137             :     }
     138             : 
     139           0 :     *out_srcMap = srcMap;
     140           0 :     *out_destMap = destMap;
     141           0 :     return true;
     142             : }
     143             : 
     144             : static void
     145           0 : UnmapSrcDest(DataSourceSurface* srcSurf,
     146             :              DataSourceSurface* destSurf)
     147             : {
     148           0 :     if (srcSurf == destSurf) {
     149           0 :         srcSurf->Unmap();
     150             :     } else {
     151           0 :         srcSurf->Unmap();
     152           0 :         destSurf->Unmap();
     153             :     }
     154           0 : }
     155             : 
     156             : bool
     157           0 : gfxUtils::PremultiplyDataSurface(DataSourceSurface* srcSurf,
     158             :                                  DataSourceSurface* destSurf)
     159             : {
     160           0 :     MOZ_ASSERT(srcSurf && destSurf);
     161             : 
     162             :     DataSourceSurface::MappedSurface srcMap;
     163             :     DataSourceSurface::MappedSurface destMap;
     164           0 :     if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
     165             :         return false;
     166             : 
     167           0 :     PremultiplyData(srcMap.mData, srcMap.mStride, srcSurf->GetFormat(),
     168           0 :                     destMap.mData, destMap.mStride, destSurf->GetFormat(),
     169           0 :                     srcSurf->GetSize());
     170             : 
     171           0 :     UnmapSrcDest(srcSurf, destSurf);
     172           0 :     return true;
     173             : }
     174             : 
     175             : bool
     176           0 : gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* srcSurf,
     177             :                                    DataSourceSurface* destSurf)
     178             : {
     179           0 :     MOZ_ASSERT(srcSurf && destSurf);
     180             : 
     181             :     DataSourceSurface::MappedSurface srcMap;
     182             :     DataSourceSurface::MappedSurface destMap;
     183           0 :     if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
     184             :         return false;
     185             : 
     186           0 :     UnpremultiplyData(srcMap.mData, srcMap.mStride, srcSurf->GetFormat(),
     187           0 :                       destMap.mData, destMap.mStride, destSurf->GetFormat(),
     188           0 :                       srcSurf->GetSize());
     189             : 
     190           0 :     UnmapSrcDest(srcSurf, destSurf);
     191           0 :     return true;
     192             : }
     193             : 
     194             : static bool
     195           0 : MapSrcAndCreateMappedDest(DataSourceSurface* srcSurf,
     196             :                           RefPtr<DataSourceSurface>* out_destSurf,
     197             :                           DataSourceSurface::MappedSurface* out_srcMap,
     198             :                           DataSourceSurface::MappedSurface* out_destMap)
     199             : {
     200           0 :     MOZ_ASSERT(srcSurf);
     201           0 :     MOZ_ASSERT(out_destSurf && out_srcMap && out_destMap);
     202             : 
     203             :     // Ok, map source for reading.
     204             :     DataSourceSurface::MappedSurface srcMap;
     205           0 :     if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
     206           0 :         MOZ_ASSERT(false, "Couldn't Map srcSurf.");
     207             :         return false;
     208             :     }
     209             : 
     210             :     // Make our dest surface based on the src.
     211             :     RefPtr<DataSourceSurface> destSurf =
     212           0 :         Factory::CreateDataSourceSurfaceWithStride(srcSurf->GetSize(),
     213           0 :                                                    srcSurf->GetFormat(),
     214           0 :                                                    srcMap.mStride);
     215           0 :     if (NS_WARN_IF(!destSurf)) {
     216             :         return false;
     217             :     }
     218             : 
     219             :     DataSourceSurface::MappedSurface destMap;
     220           0 :     if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
     221           0 :         MOZ_ASSERT(false, "Couldn't Map destSurf.");
     222             :         srcSurf->Unmap();
     223             :         return false;
     224             :     }
     225             : 
     226           0 :     *out_destSurf = destSurf;
     227           0 :     *out_srcMap = srcMap;
     228           0 :     *out_destMap = destMap;
     229           0 :     return true;
     230             : }
     231             : 
     232             : already_AddRefed<DataSourceSurface>
     233           0 : gfxUtils::CreatePremultipliedDataSurface(DataSourceSurface* srcSurf)
     234             : {
     235           0 :     RefPtr<DataSourceSurface> destSurf;
     236             :     DataSourceSurface::MappedSurface srcMap;
     237             :     DataSourceSurface::MappedSurface destMap;
     238           0 :     if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
     239           0 :         MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
     240             :         RefPtr<DataSourceSurface> surface(srcSurf);
     241             :         return surface.forget();
     242             :     }
     243             : 
     244           0 :     PremultiplyData(srcMap.mData, srcMap.mStride, srcSurf->GetFormat(),
     245           0 :                     destMap.mData, destMap.mStride, destSurf->GetFormat(),
     246           0 :                     srcSurf->GetSize());
     247             : 
     248           0 :     UnmapSrcDest(srcSurf, destSurf);
     249           0 :     return destSurf.forget();
     250             : }
     251             : 
     252             : already_AddRefed<DataSourceSurface>
     253           0 : gfxUtils::CreateUnpremultipliedDataSurface(DataSourceSurface* srcSurf)
     254             : {
     255           0 :     RefPtr<DataSourceSurface> destSurf;
     256             :     DataSourceSurface::MappedSurface srcMap;
     257             :     DataSourceSurface::MappedSurface destMap;
     258           0 :     if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
     259           0 :         MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
     260             :         RefPtr<DataSourceSurface> surface(srcSurf);
     261             :         return surface.forget();
     262             :     }
     263             : 
     264           0 :     UnpremultiplyData(srcMap.mData, srcMap.mStride, srcSurf->GetFormat(),
     265           0 :                       destMap.mData, destMap.mStride, destSurf->GetFormat(),
     266           0 :                       srcSurf->GetSize());
     267             : 
     268           0 :     UnmapSrcDest(srcSurf, destSurf);
     269           0 :     return destSurf.forget();
     270             : }
     271             : 
     272             : void
     273           0 : gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength)
     274             : {
     275           0 :     MOZ_ASSERT((aLength % 4) == 0, "Loop below will pass srcEnd!");
     276           0 :     SwizzleData(aData, aLength, SurfaceFormat::B8G8R8A8,
     277             :                 aData, aLength, SurfaceFormat::R8G8B8A8,
     278           0 :                 IntSize(aLength / 4, 1));
     279           0 : }
     280             : 
     281             : #if !defined(MOZ_GFX_OPTIMIZE_MOBILE)
     282             : /**
     283             :  * This returns the fastest operator to use for solid surfaces which have no
     284             :  * alpha channel or their alpha channel is uniformly opaque.
     285             :  * This differs per render mode.
     286             :  */
     287             : static CompositionOp
     288             : OptimalFillOp()
     289             : {
     290             : #ifdef XP_WIN
     291             :     if (gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend()) {
     292             :         // D2D -really- hates operator source.
     293             :         return CompositionOp::OP_OVER;
     294             :     }
     295             : #endif
     296             :     return CompositionOp::OP_SOURCE;
     297             : }
     298             : 
     299             : // EXTEND_PAD won't help us here; we have to create a temporary surface to hold
     300             : // the subimage of pixels we're allowed to sample.
     301             : static already_AddRefed<gfxDrawable>
     302           0 : CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
     303             :                                  gfxContext* aContext,
     304             :                                  const ImageRegion& aRegion,
     305             :                                  const SurfaceFormat aFormat,
     306             :                                  bool aUseOptimalFillOp)
     307             : {
     308           0 :     AUTO_PROFILER_LABEL("CreateSamplingRestrictedDrawable", GRAPHICS);
     309             : 
     310           0 :     DrawTarget* destDrawTarget = aContext->GetDrawTarget();
     311           0 :     if (destDrawTarget->GetBackendType() == BackendType::DIRECT2D1_1) {
     312             :       return nullptr;
     313             :     }
     314             : 
     315           0 :     gfxRect clipExtents = aContext->GetClipExtents();
     316             : 
     317             :     // Inflate by one pixel because bilinear filtering will sample at most
     318             :     // one pixel beyond the computed image pixel coordinate.
     319           0 :     clipExtents.Inflate(1.0);
     320             : 
     321           0 :     gfxRect needed = aRegion.IntersectAndRestrict(clipExtents);
     322           0 :     needed.RoundOut();
     323             : 
     324             :     // if 'needed' is empty, nothing will be drawn since aFill
     325             :     // must be entirely outside the clip region, so it doesn't
     326             :     // matter what we do here, but we should avoid trying to
     327             :     // create a zero-size surface.
     328           0 :     if (needed.IsEmpty())
     329             :         return nullptr;
     330             : 
     331           0 :     IntSize size(int32_t(needed.Width()), int32_t(needed.Height()));
     332             : 
     333             :     RefPtr<DrawTarget> target =
     334           0 :       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, aFormat);
     335           0 :     if (!target || !target->IsValid()) {
     336             :       return nullptr;
     337             :     }
     338             : 
     339           0 :     RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(target);
     340           0 :     MOZ_ASSERT(tmpCtx); // already checked the target above
     341             : 
     342           0 :     if (aUseOptimalFillOp) {
     343           0 :         tmpCtx->SetOp(OptimalFillOp());
     344             :     }
     345           0 :     aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), ExtendMode::REPEAT,
     346             :                     SamplingFilter::LINEAR,
     347           0 :                     1.0, gfxMatrix::Translation(needed.TopLeft()));
     348           0 :     RefPtr<SourceSurface> surface = target->Snapshot();
     349             : 
     350           0 :     RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size, gfxMatrix::Translation(-needed.TopLeft()));
     351           0 :     return drawable.forget();
     352             : }
     353             : #endif // !MOZ_GFX_OPTIMIZE_MOBILE
     354             : 
     355             : /* These heuristics are based on Source/WebCore/platform/graphics/skia/ImageSkia.cpp:computeResamplingMode() */
     356             : #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     357             : static SamplingFilter ReduceResamplingFilter(SamplingFilter aSamplingFilter,
     358             :                                              int aImgWidth, int aImgHeight,
     359             :                                              float aSourceWidth, float aSourceHeight)
     360             : {
     361             :     // Images smaller than this in either direction are considered "small" and
     362             :     // are not resampled ever (see below).
     363             :     const int kSmallImageSizeThreshold = 8;
     364             : 
     365             :     // The amount an image can be stretched in a single direction before we
     366             :     // say that it is being stretched so much that it must be a line or
     367             :     // background that doesn't need resampling.
     368             :     const float kLargeStretch = 3.0f;
     369             : 
     370             :     if (aImgWidth <= kSmallImageSizeThreshold
     371             :         || aImgHeight <= kSmallImageSizeThreshold) {
     372             :         // Never resample small images. These are often used for borders and
     373             :         // rules (think 1x1 images used to make lines).
     374             :         return SamplingFilter::POINT;
     375             :     }
     376             : 
     377             :     if (aImgHeight * kLargeStretch <= aSourceHeight || aImgWidth * kLargeStretch <= aSourceWidth) {
     378             :         // Large image tiling detected.
     379             : 
     380             :         // Don't resample if it is being tiled a lot in only one direction.
     381             :         // This is trying to catch cases where somebody has created a border
     382             :         // (which might be large) and then is stretching it to fill some part
     383             :         // of the page.
     384             :         if (fabs(aSourceWidth - aImgWidth)/aImgWidth < 0.5 || fabs(aSourceHeight - aImgHeight)/aImgHeight < 0.5)
     385             :             return SamplingFilter::POINT;
     386             : 
     387             :         // The image is growing a lot and in more than one direction. Resampling
     388             :         // is slow and doesn't give us very much when growing a lot.
     389             :         return aSamplingFilter;
     390             :     }
     391             : 
     392             :     /* Some notes on other heuristics:
     393             :        The Skia backend also uses nearest for backgrounds that are stretched by
     394             :        a large amount. I'm not sure this is common enough for us to worry about
     395             :        now. It also uses nearest for backgrounds/avoids high quality for images
     396             :        that are very slightly scaled.  I'm also not sure that very slightly
     397             :        scaled backgrounds are common enough us to worry about.
     398             : 
     399             :        We don't currently have much support for doing high quality interpolation.
     400             :        The only place this currently happens is on Quartz and we don't have as
     401             :        much control over it as would be needed. Webkit avoids using high quality
     402             :        resampling during load. It also avoids high quality if the transformation
     403             :        is not just a scale and translation
     404             : 
     405             :        WebKit bug #40045 added code to avoid resampling different parts
     406             :        of an image with different methods by using a resampling hint size.
     407             :        It currently looks unused in WebKit but it's something to watch out for.
     408             :     */
     409             : 
     410             :     return aSamplingFilter;
     411             : }
     412             : #else
     413             : static SamplingFilter ReduceResamplingFilter(SamplingFilter aSamplingFilter,
     414             :                                              int aImgWidth, int aImgHeight,
     415             :                                              int aSourceWidth, int aSourceHeight)
     416             : {
     417             :     // Just pass the filter through unchanged
     418             :     return aSamplingFilter;
     419             : }
     420             : #endif
     421             : 
     422             : #ifdef MOZ_WIDGET_COCOA
     423             : // Only prescale a temporary surface if we're going to repeat it often.
     424             : // Scaling is expensive on OS X and without prescaling, we'd scale
     425             : // every tile of the repeated rect. However, using a temp surface also potentially uses
     426             : // more memory if the scaled image is large. So only prescale on a temp
     427             : // surface if we know we're going to repeat the image in either the X or Y axis
     428             : // multiple times.
     429             : static bool
     430             : ShouldUseTempSurface(Rect aImageRect, Rect aNeededRect)
     431             : {
     432             :   int repeatX = aNeededRect.width / aImageRect.width;
     433             :   int repeatY = aNeededRect.height / aImageRect.height;
     434             :   return (repeatX >= 5) || (repeatY >= 5);
     435             : }
     436             : 
     437             : static bool
     438             : PrescaleAndTileDrawable(gfxDrawable* aDrawable,
     439             :                         gfxContext* aContext,
     440             :                         const ImageRegion& aRegion,
     441             :                         Rect aImageRect,
     442             :                         const SamplingFilter aSamplingFilter,
     443             :                         const SurfaceFormat aFormat,
     444             :                         gfxFloat aOpacity,
     445             :                         ExtendMode aExtendMode)
     446             : {
     447             :   Size scaleFactor = aContext->CurrentMatrix().ScaleFactors(true);
     448             :   Matrix scaleMatrix = Matrix::Scaling(scaleFactor.width, scaleFactor.height);
     449             :   const float fuzzFactor = 0.01;
     450             : 
     451             :   // If we aren't scaling or translating, don't go down this path
     452             :   if ((FuzzyEqual(scaleFactor.width, 1.0, fuzzFactor) &&
     453             :       FuzzyEqual(scaleFactor.width, 1.0, fuzzFactor)) ||
     454             :       aContext->CurrentMatrix().HasNonAxisAlignedTransform()) {
     455             :     return false;
     456             :   }
     457             : 
     458             :   gfxRect clipExtents = aContext->GetClipExtents();
     459             : 
     460             :   // Inflate by one pixel because bilinear filtering will sample at most
     461             :   // one pixel beyond the computed image pixel coordinate.
     462             :   clipExtents.Inflate(1.0);
     463             : 
     464             :   gfxRect needed = aRegion.IntersectAndRestrict(clipExtents);
     465             :   Rect scaledNeededRect = scaleMatrix.TransformBounds(ToRect(needed));
     466             :   scaledNeededRect.RoundOut();
     467             :   if (scaledNeededRect.IsEmpty()) {
     468             :     return false;
     469             :   }
     470             : 
     471             :   Rect scaledImageRect = scaleMatrix.TransformBounds(aImageRect);
     472             :   if (!ShouldUseTempSurface(scaledImageRect, scaledNeededRect)) {
     473             :     return false;
     474             :   }
     475             : 
     476             :   IntSize scaledImageSize((int32_t)scaledImageRect.width,
     477             :                           (int32_t)scaledImageRect.height);
     478             :   if (scaledImageSize.width != scaledImageRect.width ||
     479             :       scaledImageSize.height != scaledImageRect.height) {
     480             :     // If the scaled image isn't pixel aligned, we'll get artifacts
     481             :     // so we have to take the slow path.
     482             :     return false;
     483             :   }
     484             : 
     485             :   RefPtr<DrawTarget> scaledDT =
     486             :     gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(scaledImageSize, aFormat);
     487             :   if (!scaledDT || !scaledDT->IsValid()) {
     488             :     return false;
     489             :   }
     490             : 
     491             :   RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(scaledDT);
     492             :   MOZ_ASSERT(tmpCtx); // already checked the target above
     493             : 
     494             :   scaledDT->SetTransform(scaleMatrix);
     495             :   gfxRect gfxImageRect(aImageRect.x, aImageRect.y, aImageRect.width, aImageRect.height);
     496             : 
     497             :   // Since this is just the scaled image, we don't want to repeat anything yet.
     498             :   aDrawable->Draw(tmpCtx, gfxImageRect, ExtendMode::CLAMP, aSamplingFilter, 1.0, gfxMatrix());
     499             : 
     500             :   RefPtr<SourceSurface> scaledImage = scaledDT->Snapshot();
     501             : 
     502             :   {
     503             :     gfxContextMatrixAutoSaveRestore autoSR(aContext);
     504             :     Matrix withoutScale = aContext->CurrentMatrix();
     505             :     DrawTarget* destDrawTarget = aContext->GetDrawTarget();
     506             : 
     507             :     // The translation still is in scaled units
     508             :     withoutScale.PreScale(1.0 / scaleFactor.width, 1.0 / scaleFactor.height);
     509             :     aContext->SetMatrix(withoutScale);
     510             : 
     511             :     DrawOptions drawOptions(aOpacity, aContext->CurrentOp(),
     512             :                             aContext->CurrentAntialiasMode());
     513             : 
     514             :     SurfacePattern scaledImagePattern(scaledImage, aExtendMode,
     515             :                                       Matrix(), aSamplingFilter);
     516             :     destDrawTarget->FillRect(scaledNeededRect, scaledImagePattern, drawOptions);
     517             :   }
     518             :   return true;
     519             : }
     520             : #endif // MOZ_WIDGET_COCOA
     521             : 
     522             : /* static */ void
     523          33 : gfxUtils::DrawPixelSnapped(gfxContext*         aContext,
     524             :                            gfxDrawable*        aDrawable,
     525             :                            const gfxSize&      aImageSize,
     526             :                            const ImageRegion&  aRegion,
     527             :                            const SurfaceFormat aFormat,
     528             :                            SamplingFilter      aSamplingFilter,
     529             :                            uint32_t            aImageFlags,
     530             :                            gfxFloat            aOpacity,
     531             :                            bool                aUseOptimalFillOp)
     532             : {
     533           0 :     AUTO_PROFILER_LABEL("gfxUtils::DrawPixelSnapped", GRAPHICS);
     534             : 
     535           0 :     gfxRect imageRect(gfxPoint(0, 0), aImageSize);
     536          33 :     gfxRect region(aRegion.Rect());
     537          33 :     ExtendMode extendMode = aRegion.GetExtendMode();
     538             : 
     539           0 :     RefPtr<gfxDrawable> drawable = aDrawable;
     540             : 
     541             :     aSamplingFilter =
     542             :       ReduceResamplingFilter(aSamplingFilter,
     543          33 :                              imageRect.Width(), imageRect.Height(),
     544          66 :                              region.Width(), region.Height());
     545             : 
     546             :     // OK now, the hard part left is to account for the subimage sampling
     547             :     // restriction. If all the transforms involved are just integer
     548             :     // translations, then we assume no resampling will occur so there's
     549             :     // nothing to do.
     550             :     // XXX if only we had source-clipping in cairo!
     551             : 
     552          33 :     if (aContext->CurrentMatrix().HasNonIntegerTranslation()) {
     553           0 :         if ((extendMode != ExtendMode::CLAMP) || !aRegion.RestrictionContains(imageRect)) {
     554           0 :             if (drawable->DrawWithSamplingRect(aContext->GetDrawTarget(),
     555             :                                                aContext->CurrentOp(),
     556             :                                                aContext->CurrentAntialiasMode(),
     557             :                                                aRegion.Rect(),
     558             :                                                aRegion.Restriction(),
     559             :                                                extendMode, aSamplingFilter,
     560           0 :                                                aOpacity)) {
     561           0 :               return;
     562             :             }
     563             : 
     564             : #ifdef MOZ_WIDGET_COCOA
     565             :             if (PrescaleAndTileDrawable(aDrawable, aContext, aRegion,
     566             :                                         ToRect(imageRect), aSamplingFilter,
     567             :                                         aFormat, aOpacity, extendMode)) {
     568             :               return;
     569             :             }
     570             : #endif
     571             : 
     572             :             // On Mobile, we don't ever want to do this; it has the potential for
     573             :             // allocating very large temporary surfaces, especially since we'll
     574             :             // do full-page snapshots often (see bug 749426).
     575             : #if !defined(MOZ_GFX_OPTIMIZE_MOBILE)
     576             :             RefPtr<gfxDrawable> restrictedDrawable =
     577           0 :               CreateSamplingRestrictedDrawable(aDrawable, aContext,
     578             :                                                aRegion, aFormat,
     579           0 :                                                aUseOptimalFillOp);
     580           0 :             if (restrictedDrawable) {
     581           0 :               drawable.swap(restrictedDrawable);
     582             : 
     583             :               // We no longer need to tile: Either we never needed to, or we already
     584             :               // filled a surface with the tiled pattern; this surface can now be
     585             :               // drawn without tiling.
     586           0 :               extendMode = ExtendMode::CLAMP;
     587             :             }
     588             : #endif
     589             :         }
     590             :     }
     591             : 
     592           1 :     drawable->Draw(aContext, aRegion.Rect(), extendMode, aSamplingFilter,
     593          66 :                    aOpacity, gfxMatrix());
     594             : }
     595             : 
     596             : /* static */ int
     597           0 : gfxUtils::ImageFormatToDepth(gfxImageFormat aFormat)
     598             : {
     599             :     switch (aFormat) {
     600             :         case SurfaceFormat::A8R8G8B8_UINT32:
     601             :             return 32;
     602             :         case SurfaceFormat::X8R8G8B8_UINT32:
     603             :             return 24;
     604             :         case SurfaceFormat::R5G6B5_UINT16:
     605             :             return 16;
     606             :         default:
     607             :             break;
     608             :     }
     609             :     return 0;
     610             : }
     611             : 
     612             : /*static*/ void
     613           0 : gfxUtils::ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion)
     614             : {
     615           0 :   aContext->NewPath();
     616           0 :   for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
     617          12 :     const IntRect& r = iter.Get();
     618          24 :     aContext->Rectangle(gfxRect(r.X(), r.Y(), r.Width(), r.Height()));
     619             :   }
     620           4 :   aContext->Clip();
     621           0 : }
     622             : 
     623             : /*static*/ void
     624           0 : gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion)
     625             : {
     626           0 :   uint32_t numRects = aRegion.GetNumRects();
     627             :   // If there is only one rect, then the region bounds are equivalent to the
     628             :   // contents. So just use push a single clip rect with the bounds.
     629           2 :   if (numRects == 1) {
     630           2 :     aTarget->PushClipRect(Rect(aRegion.GetBounds()));
     631           2 :     return;
     632             :   }
     633             : 
     634             :   // Check if the target's transform will preserve axis-alignment and
     635             :   // pixel-alignment for each rect. For now, just handle the common case
     636             :   // of integer translations.
     637           0 :   Matrix transform = aTarget->GetTransform();
     638           0 :   if (transform.IsIntegerTranslation()) {
     639           0 :     IntPoint translation = RoundedToInt(transform.GetTranslation());
     640           0 :     AutoTArray<IntRect, 16> rects;
     641           0 :     rects.SetLength(numRects);
     642           0 :     uint32_t i = 0;
     643             :     // Build the list of transformed rects by adding in the translation.
     644           0 :     for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
     645           0 :       IntRect rect = iter.Get();
     646           0 :       rect.MoveBy(translation);
     647           0 :       rects[i++] = rect;
     648             :     }
     649           0 :     aTarget->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
     650             :   } else {
     651             :     // The transform does not produce axis-aligned rects or a rect was not
     652             :     // pixel-aligned. So just build a path with all the rects and clip to it
     653             :     // instead.
     654           0 :     RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
     655           0 :     for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
     656           0 :       AppendRectToPath(pathBuilder, Rect(iter.Get()));
     657             :     }
     658           0 :     RefPtr<Path> path = pathBuilder->Finish();
     659           0 :     aTarget->PushClip(path);
     660             :   }
     661             : }
     662             : 
     663             : /*static*/ float
     664           0 : gfxUtils::ClampToScaleFactor(float aVal, bool aRoundDown)
     665             : {
     666             :   // Arbitary scale factor limitation. We can increase this
     667             :   // for better scaling performance at the cost of worse
     668             :   // quality.
     669             :   static const float kScaleResolution = 2;
     670             : 
     671             :   // Negative scaling is just a flip and irrelevant to
     672             :   // our resolution calculation.
     673           0 :   if (aVal < 0.0) {
     674           0 :     aVal = -aVal;
     675             :   }
     676             : 
     677           0 :   bool inverse = false;
     678           0 :   if (aVal < 1.0) {
     679           0 :     inverse = true;
     680           0 :     aVal = 1 / aVal;
     681             :   }
     682             : 
     683           0 :   float power = logf(aVal)/logf(kScaleResolution);
     684             : 
     685             :   // If power is within 1e-5 of an integer, round to nearest to
     686             :   // prevent floating point errors, otherwise round up to the
     687             :   // next integer value.
     688           0 :   if (fabs(power - NS_round(power)) < 1e-5) {
     689           0 :     power = NS_round(power);
     690             :   // Use floor when we are either inverted or rounding down, but
     691             :   // not both.
     692           0 :   } else if (inverse != aRoundDown) {
     693           0 :     power = floor(power);
     694             :   // Otherwise, ceil when we are not inverted and not rounding
     695             :   // down, or we are inverted and rounding down.
     696             :   } else {
     697           0 :     power = ceil(power);
     698             :   }
     699             : 
     700           0 :   float scale = powf(kScaleResolution, power);
     701             : 
     702           0 :   if (inverse) {
     703           0 :     scale = 1 / scale;
     704             :   }
     705             : 
     706           0 :   return scale;
     707             : }
     708             : 
     709             : gfxMatrix
     710           6 : gfxUtils::TransformRectToRect(const gfxRect& aFrom, const gfxPoint& aToTopLeft,
     711             :                               const gfxPoint& aToTopRight, const gfxPoint& aToBottomRight)
     712             : {
     713           0 :   gfxMatrix m;
     714           0 :   if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) {
     715             :     // Not a rotation, so xy and yx are zero
     716           6 :     m._21 = m._12 = 0.0;
     717           0 :     m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.Width();
     718           6 :     m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.Height();
     719           0 :     m._31 = aToTopLeft.x - m._11*aFrom.X();
     720           0 :     m._32 = aToTopLeft.y - m._22*aFrom.Y();
     721             :   } else {
     722           0 :     NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x,
     723             :                  "Destination rectangle not axis-aligned");
     724           0 :     m._11 = m._22 = 0.0;
     725           0 :     m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.Height();
     726           0 :     m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.Width();
     727           0 :     m._31 = aToTopLeft.x - m._21*aFrom.Y();
     728           0 :     m._32 = aToTopLeft.y - m._12*aFrom.X();
     729             :   }
     730           6 :   return m;
     731             : }
     732             : 
     733             : Matrix
     734           0 : gfxUtils::TransformRectToRect(const gfxRect& aFrom, const IntPoint& aToTopLeft,
     735             :                               const IntPoint& aToTopRight, const IntPoint& aToBottomRight)
     736             : {
     737           0 :   Matrix m;
     738           0 :   if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) {
     739             :     // Not a rotation, so xy and yx are zero
     740           0 :     m._12 = m._21 = 0.0;
     741           0 :     m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.Width();
     742           0 :     m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.Height();
     743           0 :     m._31 = aToTopLeft.x - m._11*aFrom.X();
     744           0 :     m._32 = aToTopLeft.y - m._22*aFrom.Y();
     745             :   } else {
     746           0 :     NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x,
     747             :                  "Destination rectangle not axis-aligned");
     748           0 :     m._11 = m._22 = 0.0;
     749           0 :     m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.Height();
     750           0 :     m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.Width();
     751           0 :     m._31 = aToTopLeft.x - m._21*aFrom.Y();
     752           0 :     m._32 = aToTopLeft.y - m._12*aFrom.X();
     753             :   }
     754           0 :   return m;
     755             : }
     756             : 
     757             : /* This function is sort of shitty. We truncate doubles
     758             :  * to ints then convert those ints back to doubles to make sure that
     759             :  * they equal the doubles that we got in. */
     760             : bool
     761          43 : gfxUtils::GfxRectToIntRect(const gfxRect& aIn, IntRect* aOut)
     762             : {
     763         301 :   *aOut = IntRect(int32_t(aIn.X()), int32_t(aIn.Y()),
     764          86 :   int32_t(aIn.Width()), int32_t(aIn.Height()));
     765          86 :   return gfxRect(aOut->X(), aOut->Y(), aOut->Width(), aOut->Height()).IsEqualEdges(aIn);
     766             : }
     767             : 
     768             : /* Clamp r to CAIRO_COORD_MIN .. CAIRO_COORD_MAX
     769             :  * these are to be device coordinates.
     770             :  *
     771             :  * Cairo is currently using 24.8 fixed point,
     772             :  * so -2^24 .. 2^24-1 is our valid
     773             :  */
     774             : /*static*/ void
     775           0 : gfxUtils::ConditionRect(gfxRect& aRect)
     776             : {
     777             : #define CAIRO_COORD_MAX (16777215.0)
     778             : #define CAIRO_COORD_MIN (-16777216.0)
     779             :   // if either x or y is way out of bounds;
     780             :   // note that we don't handle negative w/h here
     781           0 :   if (aRect.X() > CAIRO_COORD_MAX) {
     782           0 :     aRect.SetRectX(CAIRO_COORD_MAX, 0.0);
     783             :   }
     784             : 
     785           0 :   if (aRect.Y() > CAIRO_COORD_MAX) {
     786           0 :     aRect.SetRectY(CAIRO_COORD_MAX, 0.0);
     787             :   }
     788             : 
     789           0 :   if (aRect.X() < CAIRO_COORD_MIN) {
     790           0 :     aRect.SetWidth(aRect.XMost() - CAIRO_COORD_MIN);
     791           0 :     if (aRect.Width() < 0.0) {
     792           0 :       aRect.SetWidth(0.0);
     793             :     }
     794           0 :     aRect.MoveToX(CAIRO_COORD_MIN);
     795             :   }
     796             : 
     797           0 :   if (aRect.Y() < CAIRO_COORD_MIN) {
     798           0 :     aRect.SetHeight(aRect.YMost() - CAIRO_COORD_MIN);
     799           0 :     if (aRect.Height() < 0.0) {
     800           0 :       aRect.SetHeight(0.0);
     801             :     }
     802           0 :     aRect.MoveToY(CAIRO_COORD_MIN);
     803             :   }
     804             : 
     805           0 :   if (aRect.XMost() > CAIRO_COORD_MAX) {
     806           0 :     aRect.SetRightEdge(CAIRO_COORD_MAX);
     807             :   }
     808             : 
     809           0 :   if (aRect.YMost() > CAIRO_COORD_MAX) {
     810           0 :     aRect.SetBottomEdge(CAIRO_COORD_MAX);
     811             :   }
     812             : #undef CAIRO_COORD_MAX
     813             : #undef CAIRO_COORD_MIN
     814           0 : }
     815             : 
     816             : /*static*/ gfxQuad
     817           0 : gfxUtils::TransformToQuad(const gfxRect& aRect,
     818             :                           const mozilla::gfx::Matrix4x4 &aMatrix)
     819             : {
     820           0 :   gfxPoint points[4];
     821             : 
     822           0 :   points[0] = aMatrix.TransformPoint(aRect.TopLeft());
     823           0 :   points[1] = aMatrix.TransformPoint(aRect.TopRight());
     824           0 :   points[2] = aMatrix.TransformPoint(aRect.BottomRight());
     825           0 :   points[3] = aMatrix.TransformPoint(aRect.BottomLeft());
     826             : 
     827             :   // Could this ever result in lines that intersect? I don't think so.
     828           0 :   return gfxQuad(points[0], points[1], points[2], points[3]);
     829             : }
     830             : 
     831           0 : /* static */ void gfxUtils::ClearThebesSurface(gfxASurface* aSurface)
     832             : {
     833           1 :   if (aSurface->CairoStatus()) {
     834             :     return;
     835             :   }
     836           0 :   cairo_surface_t* surf = aSurface->CairoSurface();
     837           0 :   if (cairo_surface_status(surf)) {
     838             :     return;
     839             :   }
     840           0 :   cairo_t* ctx = cairo_create(surf);
     841           0 :   cairo_set_source_rgba(ctx, 0.0, 0.0, 0.0, 0.0);
     842           1 :   cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
     843           5 :   IntRect bounds(nsIntPoint(0, 0), aSurface->GetSize());
     844           1 :   cairo_rectangle(ctx, bounds.X(), bounds.Y(), bounds.Width(), bounds.Height());
     845           1 :   cairo_fill(ctx);
     846           1 :   cairo_destroy(ctx);
     847             : }
     848             : 
     849             : /* static */ already_AddRefed<DataSourceSurface>
     850           0 : gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
     851             :                                                    SurfaceFormat aFormat)
     852             : {
     853           0 :   MOZ_ASSERT(aFormat != aSurface->GetFormat(),
     854             :              "Unnecessary - and very expersive - surface format conversion");
     855             : 
     856           0 :   Rect bounds(0, 0, aSurface->GetSize().width, aSurface->GetSize().height);
     857             : 
     858           0 :   if (!aSurface->IsDataSourceSurface()) {
     859             :     // If the surface is NOT of type DATA then its data is not mapped into main
     860             :     // memory. Format conversion is probably faster on the GPU, and by doing it
     861             :     // there we can avoid any expensive uploads/readbacks except for (possibly)
     862             :     // a single readback due to the unavoidable GetDataSurface() call. Using
     863             :     // CreateOffscreenContentDrawTarget ensures the conversion happens on the
     864             :     // GPU.
     865             :     RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
     866           0 :       CreateOffscreenContentDrawTarget(aSurface->GetSize(), aFormat);
     867           0 :     if (!dt) {
     868           0 :       gfxWarning() << "gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat failed in CreateOffscreenContentDrawTarget";
     869             :       return nullptr;
     870             :     }
     871             : 
     872             :     // Using DrawSurface() here rather than CopySurface() because CopySurface
     873             :     // is optimized for memcpy and therefore isn't good for format conversion.
     874             :     // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
     875             :     // generally more optimized.
     876           0 :     dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
     877           0 :                     DrawOptions(1.0f, CompositionOp::OP_OVER));
     878           0 :     RefPtr<SourceSurface> surface = dt->Snapshot();
     879           0 :     return surface->GetDataSurface();
     880             :   }
     881             : 
     882             :   // If the surface IS of type DATA then it may or may not be in main memory
     883             :   // depending on whether or not it has been mapped yet. We have no way of
     884             :   // knowing, so we can't be sure if it's best to create a data wrapping
     885             :   // DrawTarget for the conversion or an offscreen content DrawTarget. We could
     886             :   // guess it's not mapped and create an offscreen content DrawTarget, but if
     887             :   // it is then we'll end up uploading the surface data, and most likely the
     888             :   // caller is going to be accessing the resulting surface data, resulting in a
     889             :   // readback (both very expensive operations). Alternatively we could guess
     890             :   // the data is mapped and create a data wrapping DrawTarget and, if the
     891             :   // surface is not in main memory, then we will incure a readback. The latter
     892             :   // of these two "wrong choices" is the least costly (a readback, vs an
     893             :   // upload and a readback), and more than likely the DATA surface that we've
     894             :   // been passed actually IS in main memory anyway. For these reasons it's most
     895             :   // likely best to create a data wrapping DrawTarget here to do the format
     896             :   // conversion.
     897             :   RefPtr<DataSourceSurface> dataSurface =
     898           0 :     Factory::CreateDataSourceSurface(aSurface->GetSize(), aFormat);
     899             :   DataSourceSurface::MappedSurface map;
     900           0 :   if (!dataSurface ||
     901           0 :       !dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
     902             :     return nullptr;
     903             :   }
     904             :   RefPtr<DrawTarget> dt =
     905           0 :     Factory::CreateDrawTargetForData(BackendType::CAIRO,
     906             :                                      map.mData,
     907           0 :                                      dataSurface->GetSize(),
     908             :                                      map.mStride,
     909           0 :                                      aFormat);
     910           0 :   if (!dt) {
     911           0 :     dataSurface->Unmap();
     912             :     return nullptr;
     913             :   }
     914             :   // Using DrawSurface() here rather than CopySurface() because CopySurface
     915             :   // is optimized for memcpy and therefore isn't good for format conversion.
     916             :   // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
     917             :   // generally more optimized.
     918           0 :   dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
     919           0 :                   DrawOptions(1.0f, CompositionOp::OP_OVER));
     920           0 :   dataSurface->Unmap();
     921             :   return dataSurface.forget();
     922             : }
     923             : 
     924             : const uint32_t gfxUtils::sNumFrameColors = 8;
     925             : 
     926             : /* static */ const gfx::Color&
     927           0 : gfxUtils::GetColorForFrameNumber(uint64_t aFrameNumber)
     928             : {
     929             :     static bool initialized = false;
     930           0 :     static gfx::Color colors[sNumFrameColors];
     931             : 
     932           0 :     if (!initialized) {
     933           0 :         uint32_t i = 0;
     934           0 :         colors[i++] = gfx::Color::FromABGR(0xffff0000);
     935           0 :         colors[i++] = gfx::Color::FromABGR(0xffcc00ff);
     936           0 :         colors[i++] = gfx::Color::FromABGR(0xff0066cc);
     937           0 :         colors[i++] = gfx::Color::FromABGR(0xff00ff00);
     938           0 :         colors[i++] = gfx::Color::FromABGR(0xff33ffff);
     939           0 :         colors[i++] = gfx::Color::FromABGR(0xffff0099);
     940           0 :         colors[i++] = gfx::Color::FromABGR(0xff0000ff);
     941           0 :         colors[i++] = gfx::Color::FromABGR(0xff999999);
     942             :         MOZ_ASSERT(i == sNumFrameColors);
     943           0 :         initialized = true;
     944             :     }
     945             : 
     946           0 :     return colors[aFrameNumber % sNumFrameColors];
     947             : }
     948             : 
     949             : /* static */ nsresult
     950           0 : gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
     951             :                               const nsACString& aMimeType,
     952             :                               const nsAString& aOutputOptions,
     953             :                               BinaryOrData aBinaryOrData,
     954             :                               FILE* aFile,
     955             :                               nsACString* aStrOut)
     956             : {
     957           0 :   MOZ_ASSERT(aBinaryOrData == gfxUtils::eDataURIEncode || aFile || aStrOut,
     958             :              "Copying binary encoding to clipboard not currently supported");
     959             : 
     960           0 :   const IntSize size = aSurface->GetSize();
     961           0 :   if (size.IsEmpty()) {
     962             :     return NS_ERROR_INVALID_ARG;
     963             :   }
     964           0 :   const Size floatSize(size.width, size.height);
     965             : 
     966           0 :   RefPtr<DataSourceSurface> dataSurface;
     967           0 :   if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
     968             :     // FIXME bug 995807 (B8G8R8X8), bug 831898 (R5G6B5)
     969             :     dataSurface =
     970           0 :       gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(aSurface,
     971           0 :                                                          SurfaceFormat::B8G8R8A8);
     972             :   } else {
     973           0 :     dataSurface = aSurface->GetDataSurface();
     974             :   }
     975           0 :   if (!dataSurface) {
     976             :     return NS_ERROR_FAILURE;
     977             :   }
     978             : 
     979             :   DataSourceSurface::MappedSurface map;
     980           0 :   if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
     981             :     return NS_ERROR_FAILURE;
     982             :   }
     983             : 
     984             :   nsAutoCString encoderCID(
     985           0 :     NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
     986           0 :   nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
     987           0 :   if (!encoder) {
     988             : #ifdef DEBUG
     989           0 :     int32_t w = std::min(size.width, 8);
     990           0 :     int32_t h = std::min(size.height, 8);
     991           0 :     printf("Could not create encoder. Top-left %dx%d pixels contain:\n", w, h);
     992           0 :     for (int32_t y = 0; y < h; ++y) {
     993           0 :       for (int32_t x = 0; x < w; ++x) {
     994           0 :         printf("%x ", reinterpret_cast<uint32_t*>(map.mData)[y*map.mStride+x]);
     995             :       }
     996             :     }
     997             : #endif
     998           0 :     dataSurface->Unmap();
     999           0 :     return NS_ERROR_FAILURE;
    1000             :   }
    1001             : 
    1002           0 :   nsresult rv = encoder->InitFromData(map.mData,
    1003           0 :                                       BufferSizeFromStrideAndHeight(map.mStride, size.height),
    1004           0 :                                       size.width,
    1005           0 :                                       size.height,
    1006           0 :                                       map.mStride,
    1007             :                                       imgIEncoder::INPUT_FORMAT_HOSTARGB,
    1008           0 :                                       aOutputOptions);
    1009           0 :   dataSurface->Unmap();
    1010           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1011             : 
    1012           0 :   nsCOMPtr<nsIInputStream> imgStream;
    1013           0 :   CallQueryInterface(encoder.get(), getter_AddRefs(imgStream));
    1014           0 :   if (!imgStream) {
    1015             :     return NS_ERROR_FAILURE;
    1016             :   }
    1017             : 
    1018             :   uint64_t bufSize64;
    1019           0 :   rv = imgStream->Available(&bufSize64);
    1020           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1021             : 
    1022           0 :   NS_ENSURE_TRUE(bufSize64 < UINT32_MAX - 16, NS_ERROR_FAILURE);
    1023             : 
    1024           0 :   uint32_t bufSize = (uint32_t)bufSize64;
    1025             : 
    1026             :   // ...leave a little extra room so we can call read again and make sure we
    1027             :   // got everything. 16 bytes for better padding (maybe)
    1028           0 :   bufSize += 16;
    1029           0 :   uint32_t imgSize = 0;
    1030           0 :   Vector<char> imgData;
    1031           0 :   if (!imgData.initCapacity(bufSize)) {
    1032             :     return NS_ERROR_OUT_OF_MEMORY;
    1033             :   }
    1034           0 :   uint32_t numReadThisTime = 0;
    1035           0 :   while ((rv = imgStream->Read(imgData.begin() + imgSize,
    1036             :                                bufSize - imgSize,
    1037           0 :                                &numReadThisTime)) == NS_OK && numReadThisTime > 0)
    1038             :   {
    1039             :     // Update the length of the vector without overwriting the new data.
    1040           0 :     if (!imgData.growByUninitialized(numReadThisTime)) {
    1041             :       return NS_ERROR_OUT_OF_MEMORY;
    1042             :     }
    1043             : 
    1044           0 :     imgSize += numReadThisTime;
    1045           0 :     if (imgSize == bufSize) {
    1046             :       // need a bigger buffer, just double
    1047           0 :       bufSize *= 2;
    1048           0 :       if (!imgData.resizeUninitialized(bufSize)) {
    1049             :         return NS_ERROR_OUT_OF_MEMORY;
    1050             :       }
    1051             :     }
    1052             :   }
    1053           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1054           0 :   NS_ENSURE_TRUE(!imgData.empty(), NS_ERROR_FAILURE);
    1055             : 
    1056           0 :   if (aBinaryOrData == gfxUtils::eBinaryEncode) {
    1057           0 :     if (aFile) {
    1058           0 :       Unused << fwrite(imgData.begin(), 1, imgSize, aFile);
    1059             :     }
    1060             :     return NS_OK;
    1061             :   }
    1062             : 
    1063             :   // base 64, result will be null-terminated
    1064           0 :   nsCString encodedImg;
    1065           0 :   rv = Base64Encode(Substring(imgData.begin(), imgSize), encodedImg);
    1066           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1067             : 
    1068           0 :   nsCString stringBuf;
    1069           0 :   nsACString& string = aStrOut ? *aStrOut : stringBuf;
    1070           0 :   string.AppendLiteral("data:");
    1071           0 :   string.Append(aMimeType);
    1072           0 :   string.AppendLiteral(";base64,");
    1073           0 :   string.Append(encodedImg);
    1074             : 
    1075           0 :   if (aFile) {
    1076             : #ifdef ANDROID
    1077             :     if (aFile == stdout || aFile == stderr) {
    1078             :       // ADB logcat cuts off long strings so we will break it down
    1079             :       const char* cStr = string.BeginReading();
    1080             :       size_t len = strlen(cStr);
    1081             :       while (true) {
    1082             :         printf_stderr("IMG: %.140s\n", cStr);
    1083             :         if (len <= 140)
    1084             :           break;
    1085             :         len -= 140;
    1086             :         cStr += 140;
    1087             :       }
    1088             :     }
    1089             : #endif
    1090           0 :     fprintf(aFile, "%s", string.BeginReading());
    1091           0 :   } else if (!aStrOut) {
    1092           0 :     nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
    1093           0 :     if (clipboard) {
    1094           0 :       clipboard->CopyString(NS_ConvertASCIItoUTF16(string));
    1095             :     }
    1096             :   }
    1097           0 :   return NS_OK;
    1098             : }
    1099             : 
    1100             : static nsCString
    1101           0 : EncodeSourceSurfaceAsPNGURI(SourceSurface* aSurface)
    1102             : {
    1103           0 :   nsCString string;
    1104           0 :   gfxUtils::EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
    1105           0 :                                 EmptyString(), gfxUtils::eDataURIEncode,
    1106           0 :                                 nullptr, &string);
    1107           0 :   return string;
    1108             : }
    1109             : 
    1110             : // https://jdashg.github.io/misc/colors/from-coeffs.html
    1111             : const float kBT601NarrowYCbCrToRGB_RowMajor[16] = {
    1112             :   1.16438f, 0.00000f, 1.59603f,-0.87420f,
    1113             :   1.16438f,-0.39176f,-0.81297f, 0.53167f,
    1114             :   1.16438f, 2.01723f, 0.00000f,-1.08563f,
    1115             :   0.00000f, 0.00000f, 0.00000f, 1.00000f
    1116             : };
    1117             : const float kBT709NarrowYCbCrToRGB_RowMajor[16] = {
    1118             :   1.16438f, 0.00000f, 1.79274f,-0.97295f,
    1119             :   1.16438f,-0.21325f,-0.53291f, 0.30148f,
    1120             :   1.16438f, 2.11240f, 0.00000f,-1.13340f,
    1121             :   0.00000f, 0.00000f, 0.00000f, 1.00000f
    1122             : };
    1123             : 
    1124             : /* static */ const float*
    1125           0 : gfxUtils::YuvToRgbMatrix4x3RowMajor(YUVColorSpace aYUVColorSpace)
    1126             : {
    1127             :   #define X(x) { x[0], x[1], x[ 2], 0.0f, \
    1128             :                  x[4], x[5], x[ 6], 0.0f, \
    1129             :                  x[8], x[9], x[10], 0.0f }
    1130             : 
    1131             :   static const float rec601[12] = X(kBT601NarrowYCbCrToRGB_RowMajor);
    1132             :   static const float rec709[12] = X(kBT709NarrowYCbCrToRGB_RowMajor);
    1133             : 
    1134             :   #undef X
    1135             : 
    1136           0 :   switch (aYUVColorSpace) {
    1137             :   case YUVColorSpace::BT601:
    1138             :     return rec601;
    1139             :   case YUVColorSpace::BT709:
    1140           0 :     return rec709;
    1141             :   default: // YUVColorSpace::UNKNOWN
    1142           0 :     MOZ_ASSERT(false, "unknown aYUVColorSpace");
    1143             :     return rec601;
    1144             :   }
    1145             : }
    1146             : 
    1147             : /* static */ const float*
    1148           0 : gfxUtils::YuvToRgbMatrix3x3ColumnMajor(YUVColorSpace aYUVColorSpace)
    1149             : {
    1150             :   #define X(x) { x[0], x[4], x[ 8], \
    1151             :                  x[1], x[5], x[ 9], \
    1152             :                  x[2], x[6], x[10] }
    1153             : 
    1154             :   static const float rec601[9] = X(kBT601NarrowYCbCrToRGB_RowMajor);
    1155             :   static const float rec709[9] = X(kBT709NarrowYCbCrToRGB_RowMajor);
    1156             : 
    1157             :   #undef X
    1158             : 
    1159           0 :   switch (aYUVColorSpace) {
    1160             :   case YUVColorSpace::BT601:
    1161             :     return rec601;
    1162             :   case YUVColorSpace::BT709:
    1163           0 :     return rec709;
    1164             :   default: // YUVColorSpace::UNKNOWN
    1165           0 :     MOZ_ASSERT(false, "unknown aYUVColorSpace");
    1166             :     return rec601;
    1167             :   }
    1168             : }
    1169             : 
    1170             : /* static */ const float*
    1171           0 : gfxUtils::YuvToRgbMatrix4x4ColumnMajor(YUVColorSpace aYUVColorSpace)
    1172             : {
    1173             :   #define X(x) { x[0], x[4], x[ 8], x[12], \
    1174             :                  x[1], x[5], x[ 9], x[13], \
    1175             :                  x[2], x[6], x[10], x[14], \
    1176             :                  x[3], x[7], x[11], x[15] }
    1177             : 
    1178             :   static const float rec601[16] = X(kBT601NarrowYCbCrToRGB_RowMajor);
    1179             :   static const float rec709[16] = X(kBT709NarrowYCbCrToRGB_RowMajor);
    1180             : 
    1181             :   #undef X
    1182             : 
    1183           0 :   switch (aYUVColorSpace) {
    1184             :   case YUVColorSpace::BT601:
    1185             :     return rec601;
    1186             :   case YUVColorSpace::BT709:
    1187           0 :     return rec709;
    1188             :   default: // YUVColorSpace::UNKNOWN
    1189           0 :     MOZ_ASSERT(false, "unknown aYUVColorSpace");
    1190             :     return rec601;
    1191             :   }
    1192             : }
    1193             : 
    1194             : /* static */ void
    1195           0 : gfxUtils::WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile)
    1196             : {
    1197           0 :   WriteAsPNG(aSurface, NS_ConvertUTF16toUTF8(aFile).get());
    1198           0 : }
    1199             : 
    1200             : /* static */ void
    1201           0 : gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile)
    1202             : {
    1203           0 :   FILE* file = fopen(aFile, "wb");
    1204             : 
    1205           0 :   if (!file) {
    1206             :     // Maybe the directory doesn't exist; try creating it, then fopen again.
    1207           0 :     nsresult rv = NS_ERROR_FAILURE;
    1208           0 :     nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
    1209           0 :     if (comFile) {
    1210           0 :       NS_ConvertUTF8toUTF16 utf16path((nsDependentCString(aFile)));
    1211           0 :       rv = comFile->InitWithPath(utf16path);
    1212           0 :       if (NS_SUCCEEDED(rv)) {
    1213           0 :         nsCOMPtr<nsIFile> dirPath;
    1214           0 :         comFile->GetParent(getter_AddRefs(dirPath));
    1215           0 :         if (dirPath) {
    1216           0 :           rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
    1217           0 :           if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
    1218           0 :             file = fopen(aFile, "wb");
    1219             :           }
    1220             :         }
    1221             :       }
    1222             :     }
    1223           0 :     if (!file) {
    1224           0 :       NS_WARNING("Failed to open file to create PNG!");
    1225           0 :       return;
    1226             :     }
    1227             :   }
    1228             : 
    1229           0 :   EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
    1230           0 :                       EmptyString(), eBinaryEncode, file);
    1231           0 :   fclose(file);
    1232             : }
    1233             : 
    1234             : /* static */ void
    1235           0 : gfxUtils::WriteAsPNG(DrawTarget* aDT, const nsAString& aFile)
    1236             : {
    1237           0 :   WriteAsPNG(aDT, NS_ConvertUTF16toUTF8(aFile).get());
    1238           0 : }
    1239             : 
    1240             : /* static */ void
    1241           0 : gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile)
    1242             : {
    1243           0 :   RefPtr<SourceSurface> surface = aDT->Snapshot();
    1244           0 :   if (surface) {
    1245           0 :     WriteAsPNG(surface, aFile);
    1246             :   } else {
    1247           0 :     NS_WARNING("Failed to get surface!");
    1248             :   }
    1249           0 : }
    1250             : 
    1251             : /* static */ void
    1252           0 : gfxUtils::WriteAsPNG(nsIPresShell* aShell, const char* aFile)
    1253             : {
    1254           0 :   int32_t width = 1000, height = 1000;
    1255             :   nsRect r(0, 0, aShell->GetPresContext()->DevPixelsToAppUnits(width),
    1256           0 :            aShell->GetPresContext()->DevPixelsToAppUnits(height));
    1257             : 
    1258             :   RefPtr<mozilla::gfx::DrawTarget> dt = gfxPlatform::GetPlatform()->
    1259           0 :     CreateOffscreenContentDrawTarget(IntSize(width, height),
    1260           0 :                                      SurfaceFormat::B8G8R8A8);
    1261           0 :   NS_ENSURE_TRUE(dt && dt->IsValid(), /*void*/);
    1262             : 
    1263           0 :   RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
    1264           0 :   MOZ_ASSERT(context); // already checked the draw target above
    1265           0 :   aShell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
    1266           0 :   WriteAsPNG(dt.get(), aFile);
    1267             : }
    1268             : 
    1269             : /* static */ void
    1270           0 : gfxUtils::DumpAsDataURI(SourceSurface* aSurface, FILE* aFile)
    1271             : {
    1272           0 :   EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
    1273           0 :                       EmptyString(), eDataURIEncode, aFile);
    1274           0 : }
    1275             : 
    1276             : /* static */ nsCString
    1277           0 : gfxUtils::GetAsDataURI(SourceSurface* aSurface)
    1278             : {
    1279           0 :   return EncodeSourceSurfaceAsPNGURI(aSurface);
    1280             : }
    1281             : 
    1282             : /* static */ void
    1283           0 : gfxUtils::DumpAsDataURI(DrawTarget* aDT, FILE* aFile)
    1284             : {
    1285           0 :   RefPtr<SourceSurface> surface = aDT->Snapshot();
    1286           0 :   if (surface) {
    1287           0 :     DumpAsDataURI(surface, aFile);
    1288             :   } else {
    1289           0 :     NS_WARNING("Failed to get surface!");
    1290             :   }
    1291           0 : }
    1292             : 
    1293             : /* static */ nsCString
    1294           0 : gfxUtils::GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface)
    1295             : {
    1296           0 :   DataSourceSurface::ScopedMap map(aSourceSurface, DataSourceSurface::READ);
    1297           0 :   int32_t dataSize = aSourceSurface->GetSize().height * map.GetStride();
    1298           0 :   auto compressedData = MakeUnique<char[]>(LZ4::maxCompressedSize(dataSize));
    1299           0 :   if (compressedData) {
    1300           0 :     int nDataSize = LZ4::compress((char*)map.GetData(),
    1301             :                                   dataSize,
    1302           0 :                                   compressedData.get());
    1303           0 :     if (nDataSize > 0) {
    1304           0 :       nsCString encodedImg;
    1305           0 :       nsresult rv = Base64Encode(Substring(compressedData.get(), nDataSize), encodedImg);
    1306           0 :       if (rv == NS_OK) {
    1307           0 :         nsCString string("");
    1308           0 :         string.AppendPrintf("data:image/lz4bgra;base64,%i,%i,%i,",
    1309           0 :                              aSourceSurface->GetSize().width,
    1310             :                              map.GetStride(),
    1311           0 :                              aSourceSurface->GetSize().height);
    1312           0 :         string.Append(encodedImg);
    1313           0 :         return string;
    1314             :       }
    1315             :     }
    1316             :   }
    1317           0 :   return nsCString("");
    1318             : }
    1319             : 
    1320             : /* static */ nsCString
    1321           0 : gfxUtils::GetAsDataURI(DrawTarget* aDT)
    1322             : {
    1323           0 :   RefPtr<SourceSurface> surface = aDT->Snapshot();
    1324           0 :   if (surface) {
    1325           0 :     return EncodeSourceSurfaceAsPNGURI(surface);
    1326             :   } else {
    1327           0 :     NS_WARNING("Failed to get surface!");
    1328           0 :     return nsCString("");
    1329             :   }
    1330             : }
    1331             : 
    1332             : /* static */ void
    1333           0 : gfxUtils::CopyAsDataURI(SourceSurface* aSurface)
    1334             : {
    1335           0 :   EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
    1336           0 :                       EmptyString(), eDataURIEncode, nullptr);
    1337           0 : }
    1338             : 
    1339             : /* static */ void
    1340           0 : gfxUtils::CopyAsDataURI(DrawTarget* aDT)
    1341             : {
    1342           0 :   RefPtr<SourceSurface> surface = aDT->Snapshot();
    1343           0 :   if (surface) {
    1344           0 :     CopyAsDataURI(surface);
    1345             :   } else {
    1346           0 :     NS_WARNING("Failed to get surface!");
    1347             :   }
    1348           0 : }
    1349             : 
    1350             : /* static */ UniquePtr<uint8_t[]>
    1351           0 : gfxUtils::GetImageBuffer(gfx::DataSourceSurface* aSurface,
    1352             :                          bool aIsAlphaPremultiplied,
    1353             :                          int32_t* outFormat)
    1354             : {
    1355           0 :     *outFormat = 0;
    1356             : 
    1357             :     DataSourceSurface::MappedSurface map;
    1358           0 :     if (!aSurface->Map(DataSourceSurface::MapType::READ, &map))
    1359             :         return nullptr;
    1360             : 
    1361           0 :     uint32_t bufferSize = aSurface->GetSize().width * aSurface->GetSize().height * 4;
    1362           0 :     auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize);
    1363           0 :     if (!imageBuffer) {
    1364           0 :         aSurface->Unmap();
    1365             :         return nullptr;
    1366             :     }
    1367           0 :     memcpy(imageBuffer.get(), map.mData, bufferSize);
    1368             : 
    1369           0 :     aSurface->Unmap();
    1370             : 
    1371           0 :     int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
    1372           0 :     if (!aIsAlphaPremultiplied) {
    1373             :         // We need to convert to INPUT_FORMAT_RGBA, otherwise
    1374             :         // we are automatically considered premult, and unpremult'd.
    1375             :         // Yes, it is THAT silly.
    1376             :         // Except for different lossy conversions by color,
    1377             :         // we could probably just change the label, and not change the data.
    1378           0 :         gfxUtils::ConvertBGRAtoRGBA(imageBuffer.get(), bufferSize);
    1379           0 :         format = imgIEncoder::INPUT_FORMAT_RGBA;
    1380             :     }
    1381             : 
    1382           0 :     *outFormat = format;
    1383             :     return imageBuffer;
    1384             : }
    1385             : 
    1386             : /* static */ nsresult
    1387           0 : gfxUtils::GetInputStream(gfx::DataSourceSurface* aSurface,
    1388             :                          bool aIsAlphaPremultiplied,
    1389             :                          const char* aMimeType,
    1390             :                          const char16_t* aEncoderOptions,
    1391             :                          nsIInputStream** outStream)
    1392             : {
    1393           0 :     nsCString enccid("@mozilla.org/image/encoder;2?type=");
    1394           0 :     enccid += aMimeType;
    1395           0 :     nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
    1396           0 :     if (!encoder)
    1397             :         return NS_ERROR_FAILURE;
    1398             : 
    1399           0 :     int32_t format = 0;
    1400           0 :     UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(aSurface, aIsAlphaPremultiplied, &format);
    1401           0 :     if (!imageBuffer)
    1402             :         return NS_ERROR_FAILURE;
    1403             : 
    1404           0 :     return dom::ImageEncoder::GetInputStream(aSurface->GetSize().width,
    1405           0 :                                              aSurface->GetSize().height,
    1406             :                                              imageBuffer.get(), format,
    1407           0 :                                              encoder, aEncoderOptions, outStream);
    1408             : }
    1409             : 
    1410             : class GetFeatureStatusRunnable final : public dom::WorkerMainThreadRunnable
    1411             : {
    1412             : public:
    1413           0 :     GetFeatureStatusRunnable(dom::WorkerPrivate* workerPrivate,
    1414             :                              const nsCOMPtr<nsIGfxInfo>& gfxInfo,
    1415             :                              int32_t feature,
    1416             :                              nsACString& failureId,
    1417             :                              int32_t* status)
    1418           0 :       : WorkerMainThreadRunnable(workerPrivate,
    1419           0 :                                  NS_LITERAL_CSTRING("GFX :: GetFeatureStatus"))
    1420             :       , mGfxInfo(gfxInfo)
    1421             :       , mFeature(feature)
    1422             :       , mStatus(status)
    1423             :       , mFailureId(failureId)
    1424           0 :       , mNSResult(NS_OK)
    1425             :     {
    1426           0 :     }
    1427             : 
    1428           0 :     bool MainThreadRun() override
    1429             :     {
    1430           0 :       if (mGfxInfo) {
    1431           0 :         mNSResult = mGfxInfo->GetFeatureStatus(mFeature, mFailureId, mStatus);
    1432             :       }
    1433           0 :       return true;
    1434             :     }
    1435             : 
    1436             :     nsresult GetNSResult() const
    1437             :     {
    1438             :       return mNSResult;
    1439             :     }
    1440             : 
    1441             : protected:
    1442           0 :     ~GetFeatureStatusRunnable() {}
    1443             : 
    1444             : private:
    1445             :     nsCOMPtr<nsIGfxInfo> mGfxInfo;
    1446             :     int32_t mFeature;
    1447             :     int32_t* mStatus;
    1448             :     nsACString& mFailureId;
    1449             :     nsresult mNSResult;
    1450             : };
    1451             : 
    1452             : /* static */ nsresult
    1453           0 : gfxUtils::ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
    1454             :                                      int32_t feature, nsACString& failureId,
    1455             :                                      int32_t* status)
    1456             : {
    1457           0 :   if (!NS_IsMainThread()) {
    1458           0 :     dom::WorkerPrivate* workerPrivate = dom::GetCurrentThreadWorkerPrivate();
    1459             : 
    1460             :     RefPtr<GetFeatureStatusRunnable> runnable =
    1461             :       new GetFeatureStatusRunnable(workerPrivate, gfxInfo, feature, failureId,
    1462           0 :                                    status);
    1463             : 
    1464           0 :     ErrorResult rv;
    1465           0 :     runnable->Dispatch(dom::WorkerStatus::Terminating, rv);
    1466           0 :     if (rv.Failed()) {
    1467             :         // XXXbz This is totally broken, since we're supposed to just abort
    1468             :         // everything up the callstack but the callers basically eat the
    1469             :         // exception.  Ah, well.
    1470           0 :         return rv.StealNSResult();
    1471             :     }
    1472             : 
    1473           0 :     return runnable->GetNSResult();
    1474             :   }
    1475             : 
    1476           0 :   return gfxInfo->GetFeatureStatus(feature, failureId, status);
    1477             : }
    1478             : 
    1479             : #define GFX_SHADER_CHECK_BUILD_VERSION_PREF "gfx-shader-check.build-version"
    1480             : #define GFX_SHADER_CHECK_DEVICE_ID_PREF "gfx-shader-check.device-id"
    1481             : #define GFX_SHADER_CHECK_DRIVER_VERSION_PREF "gfx-shader-check.driver-version"
    1482             : 
    1483             : /* static */ void
    1484           1 : gfxUtils::RemoveShaderCacheFromDiskIfNecessary()
    1485             : {
    1486           1 :   if (!gfxVars::UseWebRenderProgramBinaryDisk()) {
    1487             :     return;
    1488             :   }
    1489             : 
    1490           0 :   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
    1491             : 
    1492             :   // Get current values
    1493           0 :   nsCString buildID(mozilla::PlatformBuildID());
    1494           0 :   nsString deviceID, driverVersion;
    1495           0 :   gfxInfo->GetAdapterDeviceID(deviceID);
    1496           0 :   gfxInfo->GetAdapterDriverVersion(driverVersion);
    1497             : 
    1498             :   // Get pref stored values
    1499           0 :   nsAutoCString buildIDChecked;
    1500           0 :   Preferences::GetCString(GFX_SHADER_CHECK_BUILD_VERSION_PREF, buildIDChecked);
    1501           0 :   nsAutoString deviceIDChecked, driverVersionChecked;
    1502           0 :   Preferences::GetString(GFX_SHADER_CHECK_DEVICE_ID_PREF, deviceIDChecked);
    1503           0 :   Preferences::GetString(GFX_SHADER_CHECK_DRIVER_VERSION_PREF, driverVersionChecked);
    1504             : 
    1505           0 :   if (buildID == buildIDChecked &&
    1506           0 :       deviceID == deviceIDChecked &&
    1507           0 :       driverVersion == driverVersionChecked) {
    1508             :       return;
    1509             :   }
    1510             : 
    1511           0 :   nsAutoString path(gfx::gfxVars::ProfDirectory());
    1512             : 
    1513           0 :   if (!wr::remove_program_binary_disk_cache(&path)) {
    1514             :     // Failed to remove program binary disk cache. The disk cache might have
    1515             :     // invalid data. Disable program binary disk cache usage.
    1516           0 :     gfxVars::SetUseWebRenderProgramBinaryDisk(false);
    1517           0 :     return;
    1518             :   }
    1519             : 
    1520           0 :   Preferences::SetCString(GFX_SHADER_CHECK_BUILD_VERSION_PREF, buildID);
    1521           0 :   Preferences::SetString(GFX_SHADER_CHECK_DEVICE_ID_PREF, deviceID);
    1522           0 :   Preferences::SetString(GFX_SHADER_CHECK_DRIVER_VERSION_PREF, driverVersion);
    1523           0 :   return;
    1524             : }
    1525             : 
    1526             : 
    1527             : /* static */ bool
    1528          53 : gfxUtils::DumpDisplayList() {
    1529           0 :   return gfxPrefs::LayoutDumpDisplayList() ||
    1530         106 :          (gfxPrefs::LayoutDumpDisplayListParent() && XRE_IsParentProcess()) ||
    1531          53 :          (gfxPrefs::LayoutDumpDisplayListContent() && XRE_IsContentProcess());
    1532             : }
    1533             : 
    1534           0 : FILE *gfxUtils::sDumpPaintFile = stderr;
    1535             : 
    1536             : namespace mozilla {
    1537             : namespace gfx {
    1538             : 
    1539          78 : Color ToDeviceColor(Color aColor)
    1540             : {
    1541             :   // aColor is pass-by-value since to get return value optimization goodness we
    1542             :   // need to return the same object from all return points in this function. We
    1543             :   // could declare a local Color variable and use that, but we might as well
    1544             :   // just use aColor.
    1545          78 :   if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
    1546           0 :     qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
    1547           0 :     if (transform) {
    1548           0 :       gfxPlatform::TransformPixel(aColor, aColor, transform);
    1549             :       // Use the original alpha to avoid unnecessary float->byte->float
    1550             :       // conversion errors
    1551             :     }
    1552             :   }
    1553           0 :   return aColor;
    1554             : }
    1555             : 
    1556             : Color ToDeviceColor(nscolor aColor)
    1557             : {
    1558             :   return ToDeviceColor(Color::FromABGR(aColor));
    1559             : }
    1560             : 
    1561             : } // namespace gfx
    1562             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952