tor-browser

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

gfxUtils.h (24544B)


      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 #ifndef GFX_UTILS_H
      7 #define GFX_UTILS_H
      8 
      9 #include "gfxMatrix.h"
     10 #include "gfxRect.h"
     11 #include "gfxTypes.h"
     12 #include "ImageTypes.h"
     13 #include "imgIContainer.h"
     14 #include "mozilla/gfx/2D.h"
     15 #include "mozilla/Maybe.h"
     16 #include "mozilla/UniquePtr.h"
     17 #include "nsColor.h"
     18 #include "nsContentUtils.h"
     19 #include "nsPrintfCString.h"
     20 #include "nsRegionFwd.h"
     21 #include "mozilla/gfx/Rect.h"
     22 #include "mozilla/CheckedInt.h"
     23 #include "mozilla/webrender/WebRenderTypes.h"
     24 #include "qcms.h"
     25 
     26 class gfxASurface;
     27 class gfxDrawable;
     28 class gfxTextRun;
     29 struct gfxQuad;
     30 class nsICookieJarSettings;
     31 class nsIInputStream;
     32 class nsIGfxInfo;
     33 
     34 namespace mozilla {
     35 namespace dom {
     36 class Element;
     37 }  // namespace dom
     38 namespace layers {
     39 class WebRenderBridgeChild;
     40 class GlyphArray;
     41 struct PlanarYCbCrData;
     42 class WebRenderCommand;
     43 }  // namespace layers
     44 namespace image {
     45 class ImageRegion;
     46 }  // namespace image
     47 namespace wr {
     48 class DisplayListBuilder;
     49 }  // namespace wr
     50 }  // namespace mozilla
     51 
     52 enum class ImageType {
     53  BMP,
     54  ICO,
     55  JPEG,
     56  PNG,
     57 };
     58 
     59 class gfxUtils {
     60 public:
     61  typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
     62  typedef mozilla::gfx::DrawTarget DrawTarget;
     63  typedef mozilla::gfx::IntPoint IntPoint;
     64  typedef mozilla::gfx::Matrix Matrix;
     65  typedef mozilla::gfx::Matrix4x4 Matrix4x4;
     66  typedef mozilla::gfx::SourceSurface SourceSurface;
     67  typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
     68  typedef mozilla::image::ImageRegion ImageRegion;
     69 
     70  /*
     71   * Premultiply or Unpremultiply aSourceSurface, writing the result
     72   * to aDestSurface or back into aSourceSurface if aDestSurface is null.
     73   *
     74   * If aDestSurface is given, it must have identical format, dimensions, and
     75   * stride as the source.
     76   *
     77   * If the source is not SurfaceFormat::A8R8G8B8_UINT32, no operation is
     78   * performed.  If aDestSurface is given, the data is copied over.
     79   */
     80  static bool PremultiplyDataSurface(DataSourceSurface* srcSurf,
     81                                     DataSourceSurface* destSurf);
     82  static bool UnpremultiplyDataSurface(DataSourceSurface* srcSurf,
     83                                       DataSourceSurface* destSurf);
     84 
     85  static already_AddRefed<DataSourceSurface> CreatePremultipliedDataSurface(
     86      DataSourceSurface* srcSurf);
     87  static already_AddRefed<DataSourceSurface> CreateUnpremultipliedDataSurface(
     88      DataSourceSurface* srcSurf);
     89 
     90  static void ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength);
     91 
     92  /**
     93   * Draw something drawable while working around limitations like bad support
     94   * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
     95   * extreme user-space-to-image-space transforms.
     96   *
     97   * The input parameters here usually come from the output of our image
     98   * snapping algorithm in nsLayoutUtils.cpp.
     99   * This method is split from nsLayoutUtils::DrawPixelSnapped to allow for
    100   * adjusting the parameters. For example, certain images with transparent
    101   * margins only have a drawable subimage. For those images, imgFrame::Draw
    102   * will tweak the rects and transforms that it gets from the pixel snapping
    103   * algorithm before passing them on to this method.
    104   */
    105  static void DrawPixelSnapped(gfxContext* aContext, gfxDrawable* aDrawable,
    106                               const gfxSize& aImageSize,
    107                               const ImageRegion& aRegion,
    108                               const mozilla::gfx::SurfaceFormat aFormat,
    109                               mozilla::gfx::SamplingFilter aSamplingFilter,
    110                               uint32_t aImageFlags = imgIContainer::FLAG_NONE,
    111                               gfxFloat aOpacity = 1.0);
    112 
    113  /**
    114   * Clip aContext to the region aRegion.
    115   */
    116  static void ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion);
    117 
    118  /**
    119   * Clip aTarget to the region aRegion.
    120   */
    121  static void ClipToRegion(mozilla::gfx::DrawTarget* aTarget,
    122                           const nsIntRegion& aRegion);
    123 
    124  /*
    125   * Convert image format to depth value
    126   */
    127  static int ImageFormatToDepth(gfxImageFormat aFormat);
    128 
    129  /**
    130   * Return the transform matrix that maps aFrom to the rectangle defined by
    131   * aToTopLeft/aToTopRight/aToBottomRight. aFrom must be
    132   * nonempty and the destination rectangle must be axis-aligned.
    133   */
    134  static gfxMatrix TransformRectToRect(const gfxRect& aFrom,
    135                                       const gfxPoint& aToTopLeft,
    136                                       const gfxPoint& aToTopRight,
    137                                       const gfxPoint& aToBottomRight);
    138 
    139  static Matrix TransformRectToRect(const gfxRect& aFrom,
    140                                    const IntPoint& aToTopLeft,
    141                                    const IntPoint& aToTopRight,
    142                                    const IntPoint& aToBottomRight);
    143 
    144  /**
    145   * If aIn can be represented exactly using an gfx::IntRect (i.e.
    146   * integer-aligned edges and coordinates in the int32_t range) then we
    147   * set aOut to that rectangle, otherwise return failure.
    148   */
    149  static bool GfxRectToIntRect(const gfxRect& aIn, mozilla::gfx::IntRect* aOut);
    150 
    151  /* Conditions this border to Cairo's max coordinate space.
    152   * The caller can check IsEmpty() after Condition() -- if it's TRUE,
    153   * the caller can possibly avoid doing any extra rendering.
    154   */
    155  static void ConditionRect(gfxRect& aRect);
    156 
    157  /*
    158   * Transform this rectangle with aMatrix, resulting in a gfxQuad.
    159   */
    160  static gfxQuad TransformToQuad(const gfxRect& aRect,
    161                                 const mozilla::gfx::Matrix4x4& aMatrix);
    162 
    163  /**
    164   * Return the smallest power of kScaleResolution (2) greater than or equal to
    165   * aVal. If aRoundDown is specified, the power of 2 will rather be less than
    166   * or equal to aVal.
    167   */
    168  static float ClampToScaleFactor(float aVal, bool aRoundDown = false);
    169 
    170  /**
    171   * We can snap layer transforms for two reasons:
    172   * 1) To avoid unnecessary resampling when a transform is a translation
    173   * by a non-integer number of pixels.
    174   * Snapping the translation to an integer number of pixels avoids
    175   * blurring the layer and can be faster to composite.
    176   * 2) When a layer is used to render a rectangular object, we need to
    177   * emulate the rendering of rectangular inactive content and snap the
    178   * edges of the rectangle to pixel boundaries. This is both to ensure
    179   * layer rendering is consistent with inactive content rendering, and to
    180   * avoid seams.
    181   * This function implements type 1 snapping. If aTransform is a 2D
    182   * translation, and this layer's layer manager has enabled snapping
    183   * (which is the default), return aTransform with the translation snapped
    184   * to nearest pixels. Otherwise just return aTransform. Call this when the
    185   * layer does not correspond to a single rectangular content object.
    186   * This function does not try to snap if aTransform has a scale, because in
    187   * that case resampling is inevitable and there's no point in trying to
    188   * avoid it. In fact snapping can cause problems because pixel edges in the
    189   * layer's content can be rendered unpredictably (jiggling) as the scale
    190   * interacts with the snapping of the translation, especially with animated
    191   * transforms.
    192   * @param aResidualTransform a transform to apply before the result transform
    193   * in order to get the results to completely match aTransform.
    194   */
    195  static Matrix4x4 SnapTransformTranslation(const Matrix4x4& aTransform,
    196                                            Matrix* aResidualTransform);
    197  static Matrix SnapTransformTranslation(const Matrix& aTransform,
    198                                         Matrix* aResidualTransform);
    199  static Matrix4x4 SnapTransformTranslation3D(const Matrix4x4& aTransform,
    200                                              Matrix* aResidualTransform);
    201  /**
    202   * See comment for SnapTransformTranslation.
    203   * This function implements type 2 snapping. If aTransform is a translation
    204   * and/or scale, transform aSnapRect by aTransform, snap to pixel boundaries,
    205   * and return the transform that maps aSnapRect to that rect. Otherwise
    206   * just return aTransform.
    207   * @param aSnapRect a rectangle whose edges should be snapped to pixel
    208   * boundaries in the destination surface.
    209   * @param aResidualTransform a transform to apply before the result transform
    210   * in order to get the results to completely match aTransform.
    211   */
    212  static Matrix4x4 SnapTransform(const Matrix4x4& aTransform,
    213                                 const gfxRect& aSnapRect,
    214                                 Matrix* aResidualTransform);
    215  static Matrix SnapTransform(const Matrix& aTransform,
    216                              const gfxRect& aSnapRect,
    217                              Matrix* aResidualTransform);
    218 
    219  /**
    220   * Clears surface to aColor (which defaults to transparent black).
    221   */
    222  static void ClearThebesSurface(gfxASurface* aSurface);
    223 
    224  static const float* YuvToRgbMatrix4x3RowMajor(
    225      mozilla::gfx::YUVColorSpace aYUVColorSpace);
    226  static const float* YuvToRgbMatrix3x3ColumnMajor(
    227      mozilla::gfx::YUVColorSpace aYUVColorSpace);
    228  static const float* YuvToRgbMatrix4x4ColumnMajor(
    229      mozilla::gfx::YUVColorSpace aYUVColorSpace);
    230 
    231  static mozilla::Maybe<mozilla::gfx::YUVColorSpace> CicpToColorSpace(
    232      const mozilla::gfx::CICP::MatrixCoefficients,
    233      const mozilla::gfx::CICP::ColourPrimaries,
    234      mozilla::LazyLogModule& aLogger);
    235 
    236  static mozilla::Maybe<mozilla::gfx::ColorSpace2> CicpToColorPrimaries(
    237      const mozilla::gfx::CICP::ColourPrimaries,
    238      mozilla::LazyLogModule& aLogger);
    239 
    240  static mozilla::Maybe<mozilla::gfx::TransferFunction> CicpToTransferFunction(
    241      const mozilla::gfx::CICP::TransferCharacteristics);
    242 
    243  /**
    244   * Creates a copy of aSurface, but having the SurfaceFormat aFormat.
    245   *
    246   * This function always creates a new surface. Do not call it if aSurface's
    247   * format is the same as aFormat. Such a non-conversion would just be an
    248   * unnecessary and wasteful copy (this function asserts to prevent that).
    249   *
    250   * This function is intended to be called by code that needs to access the
    251   * pixel data of the surface, but doesn't want to have lots of branches
    252   * to handle different pixel data formats (code which would become out of
    253   * date if and when new formats are added). Callers can use this function
    254   * to copy the surface to a specified format so that they only have to
    255   * handle pixel data in that one format.
    256   *
    257   * WARNING: There are format conversions that will not be supported by this
    258   * function. It very much depends on what the Moz2D backends support. If
    259   * the temporary B8G8R8A8 DrawTarget that this function creates has a
    260   * backend that supports DrawSurface() calls passing a surface with
    261   * aSurface's format it will work. Otherwise it will not.
    262   *
    263   *                      *** IMPORTANT PERF NOTE ***
    264   *
    265   * This function exists partly because format conversion is fraught with
    266   * non-obvious performance hazards, so we don't want Moz2D consumers to be
    267   * doing their own format conversion. Do not try to do so, or at least read
    268   * the comments in this functions implemtation. That said, the copy that
    269   * this function carries out has a cost and, although this function tries
    270   * to avoid perf hazards such as expensive uploads to/readbacks from the
    271   * GPU, it can't guarantee that it always successfully does so. Perf
    272   * critical code that can directly handle the common formats that it
    273   * encounters in a way that is cheaper than a copy-with-format-conversion
    274   * should consider doing so, and only use this function as a fallback to
    275   * handle other formats.
    276   *
    277   * XXXjwatt it would be nice if SourceSurface::GetDataSurface took a
    278   * SurfaceFormat argument (with a default argument meaning "use the
    279   * existing surface's format") and returned a DataSourceSurface in that
    280   * format. (There would then be an issue of callers maybe failing to
    281   * realize format conversion may involve expensive copying/uploading/
    282   * readback.)
    283   */
    284  static already_AddRefed<DataSourceSurface>
    285  CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
    286                                           SurfaceFormat aFormat);
    287 
    288  // Scales a SourceSurface to the new requested size.
    289  //
    290  // Asserts when the requested size is equal to the current size of the
    291  // surface.
    292  static already_AddRefed<SourceSurface> ScaleSourceSurface(
    293      SourceSurface& aSurface, const mozilla::gfx::IntSize& aTargetSize);
    294 
    295  /**
    296   * Return a color that can be used to identify a frame with a given frame
    297   * number. The colors will cycle after sNumFrameColors.  You can query colors
    298   * 0 .. sNumFrameColors-1 to get all the colors back.
    299   */
    300  static const mozilla::gfx::DeviceColor& GetColorForFrameNumber(
    301      uint64_t aFrameNumber);
    302  static const uint32_t sNumFrameColors;
    303 
    304  enum BinaryOrData { eBinaryEncode, eDataURIEncode };
    305 
    306  /**
    307   * Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder.
    308   * If both aFile and aString are null, the encoded data is copied to the
    309   * clipboard.
    310   *
    311   * @param aImageType The image type that the surface is to be encoded to.
    312   *   Used to create an appropriate imgIEncoder instance to do the encoding.
    313   *
    314   * @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
    315   *   the value of the |outputOptions| parameter. Callers are responsible
    316   *   for making sure that this is a reasonable value for the passed MIME-type
    317   *   (i.e. for the type of encoder that will be created).
    318   *
    319   * @aBinaryOrData Flag used to determine if the surface is simply encoded
    320   *   to the requested binary image format, or if the binary image is
    321   *   further converted to base-64 and written out as a 'data:' URI.
    322   *
    323   * @aFile If specified, the encoded data is written out to aFile.
    324   *
    325   * @aString If specified, the encoded data is written out to aString.
    326   *
    327   * TODO: Copying to the clipboard as a binary file is not currently
    328   * supported.
    329   */
    330  static nsresult EncodeSourceSurface(SourceSurface* aSurface,
    331                                      const ImageType aImageType,
    332                                      const nsAString& aOutputOptions,
    333                                      BinaryOrData aBinaryOrData, FILE* aFile,
    334                                      nsACString* aString = nullptr);
    335 
    336  /**
    337   * Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder
    338   * and returns the result as an nsIInputStream.
    339   *
    340   * @param aSurface The source surface to encode
    341   *
    342   * @param aImageType The image type that the surface is to be encoded to.
    343   *   Used to create an appropriate imgIEncoder instance to do the encoding.
    344   *
    345   * @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
    346   *   the value of the |outputOptions| parameter. Callers are responsible
    347   *   for making sure that this is a reasonable value for the passed MIME-type
    348   *   (i.e. for the type of encoder that will be created).
    349   *
    350   * @param aOutStream pointer to the output stream
    351   *
    352   */
    353  static nsresult EncodeSourceSurfaceAsStream(SourceSurface* aSurface,
    354                                              const ImageType aImageType,
    355                                              const nsAString& aOutputOptions,
    356                                              nsIInputStream** aOutStream);
    357 
    358  /**
    359   * Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder
    360   * and returns the result as a vector of bytes
    361   *
    362   * @param aImageType The image type that the surface is to be encoded to.
    363   *   Used to create an appropriate imgIEncoder instance to do the encoding.
    364   *
    365   * @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
    366   *   the value of the |outputOptions| parameter. Callers are responsible
    367   *   for making sure that this is a reasonable value for the passed MIME-type
    368   *   (i.e. for the type of encoder that will be created).
    369   *
    370   */
    371  static mozilla::Maybe<nsTArray<uint8_t>> EncodeSourceSurfaceAsBytes(
    372      SourceSurface* aSurface, const ImageType aImageType,
    373      const nsAString& aOutputOptions);
    374 
    375  /**
    376   * Write as a PNG file to the path aFile.
    377   */
    378  static void WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile);
    379  static void WriteAsPNG(SourceSurface* aSurface, const char* aFile);
    380  static void WriteAsPNG(DrawTarget* aDT, const nsAString& aFile);
    381  static void WriteAsPNG(DrawTarget* aDT, const char* aFile);
    382 
    383  /**
    384   * Dump as a PNG encoded Data URL to a FILE stream (using stdout by
    385   * default).
    386   *
    387   * Rather than giving aFile a default argument we have separate functions
    388   * to make them easier to use from a debugger.
    389   */
    390  static void DumpAsDataURI(SourceSurface* aSourceSurface, FILE* aFile);
    391  static inline void DumpAsDataURI(SourceSurface* aSourceSurface) {
    392    DumpAsDataURI(aSourceSurface, stdout);
    393  }
    394  static void DumpAsDataURI(DrawTarget* aDT, FILE* aFile);
    395  static inline void DumpAsDataURI(DrawTarget* aDT) {
    396    DumpAsDataURI(aDT, stdout);
    397  }
    398  static nsCString GetAsDataURI(SourceSurface* aSourceSurface);
    399  static nsCString GetAsDataURI(DrawTarget* aDT);
    400  static nsCString GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface);
    401 
    402  static mozilla::UniquePtr<uint8_t[]> GetImageBuffer(
    403      DataSourceSurface* aSurface, bool aIsAlphaPremultiplied,
    404      int32_t* outFormat);
    405 
    406  static mozilla::UniquePtr<uint8_t[]> GetImageBufferWithRandomNoise(
    407      DataSourceSurface* aSurface, bool aIsAlphaPremultiplied,
    408      nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal,
    409      int32_t* outFormat);
    410 
    411  static nsresult GetInputStream(DataSourceSurface* aSurface,
    412                                 bool aIsAlphaPremultiplied,
    413                                 const char* aMimeType,
    414                                 const nsAString& aEncoderOptions,
    415                                 nsIInputStream** outStream);
    416 
    417  static nsresult GetInputStreamWithRandomNoise(
    418      DataSourceSurface* aSurface, bool aIsAlphaPremultiplied,
    419      const char* aMimeType, const nsAString& aEncoderOptions,
    420      nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal,
    421      nsIInputStream** outStream);
    422 
    423  static void RemoveShaderCacheFromDiskIfNecessary();
    424 
    425  /**
    426   * Copy to the clipboard as a PNG encoded Data URL.
    427   */
    428  static void CopyAsDataURI(SourceSurface* aSourceSurface);
    429  static void CopyAsDataURI(DrawTarget* aDT);
    430 
    431  static bool DumpDisplayList();
    432 
    433  static FILE* sDumpPaintFile;
    434 };
    435 
    436 namespace mozilla {
    437 
    438 // Container for either a single element of type T, or an nsTArray<T>.
    439 // Provides a minimal subset of nsTArray's API, just enough to support use
    440 // by ContextState for the clipsAndTransforms list, and by gfxTextRun for
    441 // its mGlyphRuns.
    442 // Using this instead of a simple nsTArray avoids an extra allocation in the
    443 // common case where no more than one element is ever added to the list.
    444 // Unlike an AutoTArray<..., 1>, this class is memmovable and therefore can
    445 // be used in ContextState without breaking its movability.
    446 template <typename T>
    447 class ElementOrArray {
    448  union {
    449    T mElement;
    450    nsTArray<T> mArray;
    451  };
    452  enum class Tag : uint8_t {
    453    Element,
    454    Array,
    455  } mTag;
    456 
    457  // gfxTextRun::SortGlyphRuns and SanitizeGlyphRuns directly access the array.
    458  friend class ::gfxTextRun;
    459  nsTArray<T>& Array() {
    460    MOZ_DIAGNOSTIC_ASSERT(mTag == Tag::Array);
    461    return mArray;
    462  }
    463 
    464 public:
    465  // Construct as an empty array.
    466  ElementOrArray() : mTag(Tag::Array) { new (&mArray) nsTArray<T>(); }
    467 
    468  // For now, don't support copy/move.
    469  ElementOrArray(const ElementOrArray&) = delete;
    470  ElementOrArray(ElementOrArray&&) = delete;
    471 
    472  ElementOrArray& operator=(const ElementOrArray&) = delete;
    473  ElementOrArray& operator=(ElementOrArray&&) = delete;
    474 
    475  // Destroy the appropriate variant.
    476  ~ElementOrArray() {
    477    switch (mTag) {
    478      case Tag::Element:
    479        mElement.~T();
    480        break;
    481      case Tag::Array:
    482        mArray.~nsTArray();
    483        break;
    484    }
    485  }
    486 
    487  size_t Length() const { return mTag == Tag::Element ? 1 : mArray.Length(); }
    488 
    489  T* AppendElement(const T& aElement) {
    490    switch (mTag) {
    491      case Tag::Element: {
    492        // Move the existing element into an array, then append the new one.
    493        T temp = std::move(mElement);
    494        mElement.~T();
    495        mTag = Tag::Array;
    496        new (&mArray) nsTArray<T>();
    497        mArray.AppendElement(std::move(temp));
    498        return mArray.AppendElement(aElement);
    499      }
    500      case Tag::Array: {
    501        // If currently empty, just store the element directly.
    502        if (mArray.IsEmpty()) {
    503          mArray.~nsTArray();
    504          mTag = Tag::Element;
    505          new (&mElement) T(aElement);
    506          return &mElement;
    507        }
    508        // Otherwise, append it to the array.
    509        return mArray.AppendElement(aElement);
    510      }
    511      default:
    512        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("invalid tag");
    513    }
    514  }
    515 
    516  const T& LastElement() const {
    517    return mTag == Tag::Element ? mElement : mArray.LastElement();
    518  }
    519 
    520  T& LastElement() {
    521    return mTag == Tag::Element ? mElement : mArray.LastElement();
    522  }
    523 
    524  bool IsEmpty() const {
    525    return mTag == Tag::Element ? false : mArray.IsEmpty();
    526  }
    527 
    528  void TruncateLength(uint32_t aLength = 0) {
    529    MOZ_DIAGNOSTIC_ASSERT(aLength <= Length());
    530    switch (mTag) {
    531      case Tag::Element:
    532        if (aLength == 0) {
    533          // Destroy the single element, and convert to an empty array.
    534          mElement.~T();
    535          mTag = Tag::Array;
    536          new (&mArray) nsTArray<T>();
    537        }
    538        break;
    539      case Tag::Array:
    540        mArray.TruncateLength(aLength);
    541        break;
    542    }
    543  }
    544 
    545  void Clear() {
    546    switch (mTag) {
    547      case Tag::Element:
    548        mElement.~T();
    549        mTag = Tag::Array;
    550        new (&mArray) nsTArray<T>();
    551        break;
    552      case Tag::Array:
    553        mArray.Clear();
    554        break;
    555    }
    556  }
    557 
    558  // Convert from Array to Element storage. Only to be used when the current
    559  // state is a single-element array!
    560  void ConvertToElement() {
    561    MOZ_DIAGNOSTIC_ASSERT(mTag == Tag::Array && mArray.Length() == 1);
    562    T temp = std::move(mArray[0]);
    563    mArray.~nsTArray();
    564    mTag = Tag::Element;
    565    new (&mElement) T(std::move(temp));
    566  }
    567 
    568  const T& operator[](uint32_t aIndex) const {
    569    MOZ_DIAGNOSTIC_ASSERT(aIndex < Length());
    570    return mTag == Tag::Element ? mElement : mArray[aIndex];
    571  }
    572  T& operator[](uint32_t aIndex) {
    573    MOZ_DIAGNOSTIC_ASSERT(aIndex < Length());
    574    return mTag == Tag::Element ? mElement : mArray[aIndex];
    575  }
    576 
    577  // Simple iterators to support range-for loops.
    578  const T* begin() const {
    579    return mTag == Tag::Array ? mArray.IsEmpty() ? nullptr : &*mArray.begin()
    580                              : &mElement;
    581  }
    582  T* begin() {
    583    return mTag == Tag::Array ? mArray.IsEmpty() ? nullptr : &*mArray.begin()
    584                              : &mElement;
    585  }
    586 
    587  const T* end() const {
    588    return mTag == Tag::Array ? begin() + mArray.Length() : &mElement + 1;
    589  }
    590  T* end() {
    591    return mTag == Tag::Array ? begin() + mArray.Length() : &mElement + 1;
    592  }
    593 
    594  size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
    595    return mTag == Tag::Array ? mArray.ShallowSizeOfExcludingThis(aMallocSizeOf)
    596                              : 0;
    597  }
    598 };
    599 
    600 struct StyleAbsoluteColor;
    601 
    602 namespace gfx {
    603 
    604 /**
    605 * If the CMS mode is CMSMode::All, these functions transform the passed
    606 * color to a device color using the transform returned by
    607 * gfxPlatform::GetCMSRGBTransform().  If the CMS mode is some other value, the
    608 * color is returned unchanged (other than a type change to Moz2D Color, if
    609 * applicable).
    610 */
    611 DeviceColor ToDeviceColor(const sRGBColor&);
    612 DeviceColor ToDeviceColor(const StyleAbsoluteColor&);
    613 DeviceColor ToDeviceColor(nscolor);
    614 
    615 sRGBColor ToSRGBColor(const StyleAbsoluteColor&);
    616 
    617 /**
    618 * Performs a checked multiply of the given width, height, and bytes-per-pixel
    619 * values.
    620 */
    621 static inline CheckedInt<uint32_t> SafeBytesForBitmap(uint32_t aWidth,
    622                                                      uint32_t aHeight,
    623                                                      unsigned aBytesPerPixel) {
    624  MOZ_ASSERT(aBytesPerPixel > 0);
    625  CheckedInt<uint32_t> width = uint32_t(aWidth);
    626  CheckedInt<uint32_t> height = uint32_t(aHeight);
    627  return width * height * aBytesPerPixel;
    628 }
    629 
    630 }  // namespace gfx
    631 }  // namespace mozilla
    632 
    633 #endif