tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxUtils.cpp (60828B)


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