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

Generated by: LCOV version 1.13