tor-browser

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

COLRFonts.cpp (91242B)


      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 "COLRFonts.h"
      7 #include "gfxFontUtils.h"
      8 #include "gfxUtils.h"
      9 #include "harfbuzz/hb.h"
     10 #include "harfbuzz/hb-ot.h"
     11 #include "mozilla/gfx/Helpers.h"
     12 #include "mozilla/ScopeExit.h"
     13 #include "mozilla/StaticPrefs_gfx.h"
     14 #include "TextDrawTarget.h"
     15 
     16 #include <limits>
     17 
     18 using namespace mozilla;
     19 using namespace mozilla::gfx;
     20 
     21 namespace {  // anonymous namespace for implementation internals
     22 
     23 #pragma pack(1)  // ensure no padding is added to the COLR structs
     24 
     25 // Alias bigendian-reading types from gfxFontUtils to names used in the spec.
     26 using int16 = AutoSwap_PRInt16;
     27 using uint16 = AutoSwap_PRUint16;
     28 using int32 = AutoSwap_PRInt32;
     29 using uint32 = AutoSwap_PRUint32;
     30 using FWORD = AutoSwap_PRInt16;
     31 using UFWORD = AutoSwap_PRUint16;
     32 using Offset16 = AutoSwap_PRUint16;
     33 using Offset24 = AutoSwap_PRUint24;
     34 using Offset32 = AutoSwap_PRUint32;
     35 
     36 struct COLRv1Header;
     37 struct ClipList;
     38 struct LayerRecord;
     39 struct BaseGlyphRecord;
     40 struct DeltaSetIndexMap;
     41 struct ItemVariationStore;
     42 
     43 struct COLRHeader {
     44  uint16 version;
     45  uint16 numBaseGlyphRecords;
     46  Offset32 baseGlyphRecordsOffset;
     47  Offset32 layerRecordsOffset;
     48  uint16 numLayerRecords;
     49 
     50  const BaseGlyphRecord* GetBaseGlyphRecords() const {
     51    return reinterpret_cast<const BaseGlyphRecord*>(
     52        reinterpret_cast<const char*>(this) + baseGlyphRecordsOffset);
     53  }
     54 
     55  const LayerRecord* GetLayerRecords() const {
     56    return reinterpret_cast<const LayerRecord*>(
     57        reinterpret_cast<const char*>(this) + layerRecordsOffset);
     58  }
     59 
     60  bool Validate(uint64_t aLength) const;
     61 };
     62 
     63 struct BaseGlyphPaintRecord {
     64  uint16 glyphID;
     65  Offset32 paintOffset;
     66 };
     67 
     68 struct BaseGlyphList {
     69  uint32 numBaseGlyphPaintRecords;
     70  // BaseGlyphPaintRecord baseGlyphPaintRecords[numBaseGlyphPaintRecords];
     71  const BaseGlyphPaintRecord* baseGlyphPaintRecords() const {
     72    return reinterpret_cast<const BaseGlyphPaintRecord*>(this + 1);
     73  }
     74  bool Validate(const COLRv1Header* aHeader, uint64_t aLength) const;
     75 };
     76 
     77 struct LayerList {
     78  uint32 numLayers;
     79  // uint32 paintOffsets[numLayers];
     80  const uint32* paintOffsets() const {
     81    return reinterpret_cast<const uint32*>(this + 1);
     82  }
     83  bool Validate(const COLRv1Header* aHeader, uint64_t aLength) const;
     84 };
     85 
     86 struct COLRv1Header {
     87  COLRHeader base;
     88  Offset32 baseGlyphListOffset;
     89  Offset32 layerListOffset;
     90  Offset32 clipListOffset;
     91  Offset32 varIndexMapOffset;
     92  Offset32 itemVariationStoreOffset;
     93 
     94  bool Validate(uint64_t aLength) const;
     95 
     96  const BaseGlyphList* baseGlyphList() const {
     97    uint32_t offset = baseGlyphListOffset;
     98    if (!offset) {
     99      return nullptr;
    100    }
    101    const char* ptr = reinterpret_cast<const char*>(this) + offset;
    102    return reinterpret_cast<const struct BaseGlyphList*>(ptr);
    103  }
    104 
    105  const LayerList* layerList() const {
    106    uint32_t offset = layerListOffset;
    107    if (!offset) {
    108      return nullptr;
    109    }
    110    const char* ptr = reinterpret_cast<const char*>(this) + offset;
    111    return reinterpret_cast<const LayerList*>(ptr);
    112  }
    113 
    114  const struct ClipList* clipList() const {
    115    uint32_t offset = clipListOffset;
    116    if (!offset) {
    117      return nullptr;
    118    }
    119    const char* ptr = reinterpret_cast<const char*>(this) + offset;
    120    return reinterpret_cast<const ClipList*>(ptr);
    121  }
    122 
    123  const struct DeltaSetIndexMap* varIndexMap() const {
    124    uint32_t offset = varIndexMapOffset;
    125    if (!offset) {
    126      return nullptr;
    127    }
    128    const char* ptr = reinterpret_cast<const char*>(this) + offset;
    129    return reinterpret_cast<const DeltaSetIndexMap*>(ptr);
    130  }
    131 
    132  const struct ItemVariationStore* itemVariationStore() const {
    133    uint32_t offset = itemVariationStoreOffset;
    134    if (!offset) {
    135      return nullptr;
    136    }
    137    const char* ptr = reinterpret_cast<const char*>(this) + offset;
    138    return reinterpret_cast<const ItemVariationStore*>(ptr);
    139  }
    140 
    141  const BaseGlyphPaintRecord* GetBaseGlyphPaint(uint32_t aGlyphId) const;
    142 };
    143 
    144 struct PaintState {
    145  union {
    146    const COLRHeader* v0;
    147    const COLRv1Header* v1;
    148  } mHeader;
    149  const sRGBColor* mPalette;
    150  DrawTarget* mDrawTarget;
    151  ScaledFont* mScaledFont;
    152  const int* mCoords;
    153  DrawOptions mDrawOptions;
    154  uint32_t mCOLRLength;
    155  sRGBColor mCurrentColor;
    156  float mFontUnitsToPixels;
    157  uint16_t mNumColors;
    158  uint16_t mCoordCount;
    159  nsTArray<uint32_t>* mVisited;
    160 
    161  const char* COLRv1BaseAddr() const {
    162    return reinterpret_cast<const char*>(mHeader.v1);
    163  }
    164 
    165  DeviceColor GetColor(uint16_t aPaletteIndex, float aAlpha) const;
    166 
    167  // Convert from FUnits (either integer or Fixed 16.16) to device pixels.
    168  template <typename T>
    169  float F2P(T aPixels) const {
    170    return mFontUnitsToPixels * float(aPixels);
    171  }
    172 };
    173 
    174 DeviceColor PaintState::GetColor(uint16_t aPaletteIndex, float aAlpha) const {
    175  sRGBColor color;
    176  if (aPaletteIndex < mNumColors) {
    177    color = mPalette[uint16_t(aPaletteIndex)];
    178  } else if (aPaletteIndex == 0xffff) {
    179    color = mCurrentColor;
    180  } else {  // Palette index out of range! Return transparent black.
    181    color = sRGBColor();
    182  }
    183  color.a *= aAlpha;
    184  return ToDeviceColor(color);
    185 }
    186 
    187 static bool DispatchPaint(const PaintState& aState, uint32_t aOffset,
    188                          const Rect* aBounds /* may be nullptr if unknown */);
    189 static UniquePtr<Pattern> DispatchMakePattern(const PaintState& aState,
    190                                              uint32_t aOffset);
    191 static Rect DispatchGetBounds(const PaintState& aState, uint32_t aOffset);
    192 static Matrix DispatchGetMatrix(const PaintState& aState, uint32_t aOffset);
    193 
    194 // Variation-data types
    195 struct Fixed {
    196  enum { kFractionBits = 16 };
    197  operator float() const {
    198    return float(int32_t(value)) / float(1 << kFractionBits);
    199  }
    200  int32_t intRepr() const { return int32_t(value); }
    201 
    202 private:
    203  int32 value;
    204 };
    205 
    206 struct F2DOT14 {
    207  enum { kFractionBits = 14 };
    208  operator float() const {
    209    return float(int16_t(value)) / float(1 << kFractionBits);
    210  }
    211  int32_t intRepr() const { return int16_t(value); }
    212 
    213 private:
    214  int16 value;
    215 };
    216 
    217 // Saturating addition used for variation indexes to avoid wrap-around.
    218 static uint32_t SatAdd(uint32_t a, uint32_t b) {
    219  if (a <= std::numeric_limits<uint32_t>::max() - b) {
    220    return a + b;
    221  }
    222  return std::numeric_limits<uint32_t>::max();
    223 }
    224 
    225 struct RegionAxisCoordinates {
    226  F2DOT14 startCoord;
    227  F2DOT14 peakCoord;
    228  F2DOT14 endCoord;
    229 };
    230 
    231 struct VariationRegion {
    232  // RegionAxisCoordinates regionAxes[axisCount];
    233  const RegionAxisCoordinates* regionAxes() const {
    234    return reinterpret_cast<const RegionAxisCoordinates*>(this);
    235  }
    236 };
    237 
    238 struct VariationRegionList {
    239  uint16 axisCount;
    240  uint16 regionCount;
    241  // VariationRegion variationRegions[regionCount];
    242  const char* variationRegionsBase() const {
    243    return reinterpret_cast<const char*>(this + 1);
    244  }
    245  size_t regionSize() const {
    246    return uint16_t(axisCount) * sizeof(RegionAxisCoordinates);
    247  }
    248  const VariationRegion* getRegion(uint16_t i) const {
    249    if (i >= uint16_t(regionCount)) {
    250      return nullptr;
    251    }
    252    return reinterpret_cast<const VariationRegion*>(variationRegionsBase() +
    253                                                    i * regionSize());
    254  }
    255  bool Validate(const COLRv1Header* aHeader, uint64_t aLength) const {
    256    if (variationRegionsBase() - reinterpret_cast<const char*>(aHeader) +
    257            uint16_t(regionCount) * regionSize() >
    258        aLength) {
    259      return false;
    260    }
    261    return true;
    262  }
    263 };
    264 
    265 struct DeltaSet {
    266  // (int16 and int8)
    267  // *or*
    268  // (int32 and int16) deltaData[regionIndexCount];
    269 };
    270 
    271 struct DeltaSetIndexMap {
    272  enum { INNER_INDEX_BIT_COUNT_MASK = 0x0f, MAP_ENTRY_SIZE_MASK = 0x30 };
    273  uint8_t format;
    274  uint8_t entryFormat;
    275  union {
    276    struct {
    277      uint16 mapCount;
    278      // uint8 mapData[variable];
    279    } v0;
    280    struct {
    281      uint32 mapCount;
    282      // uint8 mapData[variable];
    283    } v1;
    284  };
    285  uint32_t entrySize() const {
    286    return (((entryFormat & MAP_ENTRY_SIZE_MASK) >> 4) + 1);
    287  }
    288  uint32_t map(uint32_t aIndex) const {
    289    uint32_t mapCount;
    290    const uint8_t* mapData;
    291    switch (format) {
    292      case 0:
    293        mapCount = uint32_t(v0.mapCount);
    294        mapData = reinterpret_cast<const uint8_t*>(&v0.mapCount) +
    295                  sizeof(v0.mapCount);
    296        break;
    297      case 1:
    298        mapCount = uint32_t(v1.mapCount);
    299        mapData = reinterpret_cast<const uint8_t*>(&v1.mapCount) +
    300                  sizeof(v1.mapCount);
    301        break;
    302      default:
    303        // unknown DeltaSetIndexMap format
    304        return aIndex;
    305    }
    306    if (!mapCount) {
    307      return aIndex;
    308    }
    309    if (aIndex >= mapCount) {
    310      aIndex = mapCount - 1;
    311    }
    312    const uint8_t* entryData = mapData + aIndex * entrySize();
    313    uint32_t entry = 0;
    314    for (uint32_t i = 0; i < entrySize(); ++i) {
    315      entry = (entry << 8) + entryData[i];
    316    }
    317    uint16_t outerIndex =
    318        entry >> ((entryFormat & INNER_INDEX_BIT_COUNT_MASK) + 1);
    319    uint16_t innerIndex =
    320        entry & ((1 << ((entryFormat & INNER_INDEX_BIT_COUNT_MASK) + 1)) - 1);
    321    return (uint32_t(outerIndex) << 16) + innerIndex;
    322  }
    323  bool Validate(const COLRv1Header* aHeader, uint64_t aLength) const;
    324 };
    325 
    326 enum EntryFormatMasks {
    327  INNER_INDEX_BIT_COUNT_MASK = 0x0f,
    328  MAP_ENTRY_SIZE_MASK = 0x30
    329 };
    330 
    331 struct ItemVariationData {
    332  enum { WORD_DELTA_COUNT_MASK = 0x7FFF, LONG_WORDS = 0x8000 };
    333  uint16 itemCount;
    334  uint16 wordDeltaCount;
    335  uint16 regionIndexCount;
    336  // uint16 regionIndexes[regionIndexCount];
    337  const uint16* regionIndexes() const {
    338    return reinterpret_cast<const uint16*>(
    339        reinterpret_cast<const char*>(this + 1));
    340  }
    341  // DeltaSet deltaSets[itemCount];
    342  const DeltaSet* deltaSets() const {
    343    return reinterpret_cast<const DeltaSet*>(
    344        reinterpret_cast<const char*>(this + 1) +
    345        uint16_t(regionIndexCount) * sizeof(uint16));
    346  }
    347  bool Validate(const COLRv1Header* aHeader, uint64_t aLength) const;
    348 };
    349 
    350 struct ItemVariationStore {
    351  uint16 format;
    352  Offset32 variationRegionListOffset;
    353  uint16 itemVariationDataCount;
    354  // Offset32 itemVariationDataOffsets[itemVariationDataCount];
    355  const Offset32* itemVariationDataOffsets() const {
    356    return reinterpret_cast<const Offset32*>(
    357        reinterpret_cast<const char*>(this + 1));
    358  }
    359  const VariationRegionList* variationRegionList() const {
    360    return reinterpret_cast<const VariationRegionList*>(
    361        reinterpret_cast<const char*>(this) + variationRegionListOffset);
    362  }
    363  bool Validate(const COLRv1Header* aHeader, uint64_t aLength) const;
    364 };
    365 
    366 static int32_t ApplyVariation(const PaintState& aState, int32_t aValue,
    367                              uint32_t aIndex) {
    368  if (aIndex == 0xffffffff) {
    369    return aValue;
    370  }
    371  const auto* store = aState.mHeader.v1->itemVariationStore();
    372  if (!store || uint16_t(store->format) != 1) {
    373    return aValue;
    374  }
    375  const DeltaSetIndexMap* map = aState.mHeader.v1->varIndexMap();
    376  uint32_t mappedIndex = map ? map->map(aIndex) : aIndex;
    377  uint16_t outerIndex = mappedIndex >> 16;
    378  uint16_t innerIndex = mappedIndex & 0xffff;
    379  const auto* itemVariationDataOffsets = store->itemVariationDataOffsets();
    380  if (mappedIndex == 0xffffffff ||
    381      outerIndex >= uint16_t(store->itemVariationDataCount) ||
    382      !itemVariationDataOffsets[outerIndex]) {
    383    return aValue;
    384  }
    385  const auto* regionList = store->variationRegionList();
    386  if (outerIndex >= uint16_t(store->itemVariationDataCount)) {
    387    return aValue;
    388  }
    389  const auto* variationData = reinterpret_cast<const ItemVariationData*>(
    390      reinterpret_cast<const char*>(store) +
    391      itemVariationDataOffsets[outerIndex]);
    392  if (innerIndex >= uint16_t(variationData->itemCount)) {
    393    return aValue;
    394  }
    395  const auto* regionIndexes = variationData->regionIndexes();
    396  uint16_t regionIndexCount = variationData->regionIndexCount;
    397  const DeltaSet* deltaSets = variationData->deltaSets();
    398  uint16_t wordDeltaCount = variationData->wordDeltaCount;
    399  bool longWords = wordDeltaCount & ItemVariationData::LONG_WORDS;
    400  wordDeltaCount &= ItemVariationData::WORD_DELTA_COUNT_MASK;
    401  uint32_t deltaSetSize = (regionIndexCount + wordDeltaCount) << longWords;
    402  const uint8_t* deltaData =
    403      reinterpret_cast<const uint8_t*>(deltaSets) + deltaSetSize * innerIndex;
    404  uint16_t deltaSize = longWords ? 4 : 2;
    405  int32_t result = aValue;
    406  for (uint16_t i = 0; i < regionIndexCount; ++i, deltaData += deltaSize) {
    407    if (i == wordDeltaCount) {
    408      deltaSize >>= 1;
    409    }
    410    const auto* region = regionList->getRegion(uint16_t(regionIndexes[i]));
    411    if (!region) {
    412      return aValue;
    413    }
    414    // XXX Should we do the calculations here in fixed-point? Check spec.
    415    float scalar = -1.0;
    416    for (uint16_t axisIndex = 0; axisIndex < uint16_t(regionList->axisCount);
    417         ++axisIndex) {
    418      const auto& axis = region->regionAxes()[axisIndex];
    419      float peak = axis.peakCoord;
    420      if (peak == 0.0) {
    421        // This axis cannot contribute to scalar.
    422        continue;
    423      }
    424      float start = axis.startCoord;
    425      float end = axis.endCoord;
    426      float value = axisIndex < aState.mCoordCount
    427                        ? float(aState.mCoords[axisIndex]) / 16384.0f
    428                        : 0.0;
    429      if (value < start || value > end) {
    430        // Out of range: this region is not applicable.
    431        scalar = -1.0;
    432        break;
    433      }
    434      if (scalar < 0.0) {
    435        scalar = 1.0;
    436      }
    437      if (value == peak) {
    438        continue;
    439      }
    440      if (value < peak && peak > start) {
    441        scalar *= (value - start) / (peak - start);
    442      } else if (value > peak && peak < end) {
    443        scalar *= (end - value) / (end - peak);
    444      }
    445    }
    446    if (scalar <= 0.0) {
    447      continue;
    448    }
    449    int32_t delta = *reinterpret_cast<const int8_t*>(deltaData);  // sign-extend
    450    for (uint16_t j = 1; j < deltaSize; ++j) {
    451      delta = (delta << 8) | deltaData[j];
    452    }
    453    delta = int32_t(floorf((float(delta) * scalar) + 0.5f));
    454    result += delta;
    455  }
    456  return result;
    457 };
    458 
    459 static float ApplyVariation(const PaintState& aState, Fixed aValue,
    460                            uint32_t aIndex) {
    461  return float(ApplyVariation(aState, aValue.intRepr(), aIndex)) /
    462         (1 << Fixed::kFractionBits);
    463 }
    464 
    465 static float ApplyVariation(const PaintState& aState, F2DOT14 aValue,
    466                            uint32_t aIndex) {
    467  return float(ApplyVariation(aState, aValue.intRepr(), aIndex)) /
    468         (1 << F2DOT14::kFractionBits);
    469 }
    470 
    471 struct ClipBoxFormat1 {
    472  enum { kFormat = 1 };
    473  uint8_t format;
    474  FWORD xMin;
    475  FWORD yMin;
    476  FWORD xMax;
    477  FWORD yMax;
    478 
    479  Rect GetRect(const PaintState& aState) const {
    480    MOZ_ASSERT(format == kFormat);
    481    int32_t x0 = int16_t(xMin);
    482    int32_t y0 = int16_t(yMin);
    483    int32_t x1 = int16_t(xMax);
    484    int32_t y1 = int16_t(yMax);
    485    // Flip the y-coordinates to map from OpenType to Moz2d space.
    486    return Rect(aState.F2P(x0), -aState.F2P(y1), aState.F2P(x1 - x0),
    487                aState.F2P(y1 - y0));
    488  }
    489 };
    490 
    491 struct ClipBoxFormat2 : public ClipBoxFormat1 {
    492  enum { kFormat = 2 };
    493  uint32 varIndexBase;
    494 
    495  Rect GetRect(const PaintState& aState) const {
    496    MOZ_ASSERT(format == kFormat);
    497    int32_t x0 = ApplyVariation(aState, int16_t(xMin), varIndexBase);
    498    int32_t y0 = ApplyVariation(aState, int16_t(yMin), SatAdd(varIndexBase, 1));
    499    int32_t x1 = ApplyVariation(aState, int16_t(xMax), SatAdd(varIndexBase, 2));
    500    int32_t y1 = ApplyVariation(aState, int16_t(yMax), SatAdd(varIndexBase, 3));
    501    return Rect(aState.F2P(x0), -aState.F2P(y1), aState.F2P(x1 - x0),
    502                aState.F2P(y1 - y0));
    503  }
    504 };
    505 
    506 struct Clip {
    507  uint16 startGlyphID;
    508  uint16 endGlyphID;
    509  Offset24 clipBoxOffset;
    510 
    511  Rect GetRect(const PaintState& aState) const {
    512    uint32_t offset = aState.mHeader.v1->clipListOffset + clipBoxOffset;
    513    const auto* box = aState.COLRv1BaseAddr() + offset;
    514    switch (*box) {
    515      case 1:
    516        return reinterpret_cast<const ClipBoxFormat1*>(box)->GetRect(aState);
    517      case 2:
    518        return reinterpret_cast<const ClipBoxFormat2*>(box)->GetRect(aState);
    519      default:
    520        // unknown ClipBoxFormat
    521        break;
    522    }
    523    return Rect();
    524  }
    525  bool Validate(const COLRv1Header* aHeader, uint64_t aLength) const;
    526 };
    527 
    528 struct ClipList {
    529  uint8_t format;
    530  uint32 numClips;
    531  // Clip clips[numClips]
    532  const Clip* clips() const { return reinterpret_cast<const Clip*>(this + 1); }
    533  const Clip* GetClip(uint32_t aGlyphId) const {
    534    auto compare = [](const void* key, const void* data) -> int {
    535      uint32_t glyphId = (uint32_t)(uintptr_t)key;
    536      const auto* clip = reinterpret_cast<const Clip*>(data);
    537      uint32_t start = uint16_t(clip->startGlyphID);
    538      uint32_t end = uint16_t(clip->endGlyphID);
    539      if (start <= glyphId && end >= glyphId) {
    540        return 0;
    541      }
    542      return start > glyphId ? -1 : 1;
    543    };
    544    return reinterpret_cast<const Clip*>(bsearch((void*)(uintptr_t)aGlyphId,
    545                                                 clips(), uint32_t(numClips),
    546                                                 sizeof(Clip), compare));
    547  }
    548  bool Validate(const COLRv1Header* aHeader, uint64_t aLength) const;
    549 };
    550 
    551 struct LayerRecord {
    552  uint16 glyphId;
    553  uint16 paletteEntryIndex;
    554 
    555  bool Paint(const PaintState& aState, float aAlpha,
    556             const Point& aPoint) const {
    557    Glyph glyph{uint16_t(glyphId), aPoint};
    558    GlyphBuffer buffer{&glyph, 1};
    559    aState.mDrawTarget->FillGlyphs(
    560        aState.mScaledFont, buffer,
    561        ColorPattern(aState.GetColor(paletteEntryIndex, aAlpha)),
    562        aState.mDrawOptions);
    563    return true;
    564  }
    565 };
    566 
    567 struct BaseGlyphRecord {
    568  uint16 glyphId;
    569  uint16 firstLayerIndex;
    570  uint16 numLayers;
    571 
    572  bool Paint(const PaintState& aState, float aAlpha,
    573             const Point& aPoint) const {
    574    uint32_t layerIndex = uint16_t(firstLayerIndex);
    575    uint32_t end = layerIndex + uint16_t(numLayers);
    576    if (end > uint16_t(aState.mHeader.v0->numLayerRecords)) {
    577      MOZ_ASSERT_UNREACHABLE("bad COLRv0 table");
    578      return false;
    579    }
    580    const auto* layers = aState.mHeader.v0->GetLayerRecords();
    581    while (layerIndex < end) {
    582      if (!layers[layerIndex].Paint(aState, aAlpha, aPoint)) {
    583        return false;
    584      }
    585      ++layerIndex;
    586    }
    587    return true;
    588  }
    589 };
    590 
    591 struct ColorStop {
    592  F2DOT14 stopOffset;
    593  uint16 paletteIndex;
    594  F2DOT14 alpha;
    595 
    596  float GetStopOffset(const PaintState& aState) const { return stopOffset; }
    597  uint16_t GetPaletteIndex() const { return paletteIndex; }
    598  float GetAlpha(const PaintState& aState) const { return alpha; }
    599 };
    600 
    601 struct VarColorStop : public ColorStop {
    602  uint32 varIndexBase;
    603 
    604  float GetStopOffset(const PaintState& aState) const {
    605    return ApplyVariation(aState, stopOffset, varIndexBase);
    606  }
    607  float GetAlpha(const PaintState& aState) const {
    608    return ApplyVariation(aState, alpha, SatAdd(varIndexBase, 1));
    609  }
    610 };
    611 
    612 template <typename T>
    613 struct ColorLineT {
    614  enum { EXTEND_PAD = 0, EXTEND_REPEAT = 1, EXTEND_REFLECT = 2 };
    615  uint8_t extend;
    616  uint16 numStops;
    617  const T* colorStops() const { return reinterpret_cast<const T*>(this + 1); }
    618 
    619  // If the color line has only one stop, return it as a simple ColorPattern.
    620  UniquePtr<Pattern> AsSolidColor(const PaintState& aState) const {
    621    if (uint16_t(numStops) != 1) {
    622      return nullptr;
    623    }
    624    const auto* stop = colorStops();
    625    return MakeUnique<ColorPattern>(
    626        aState.GetColor(stop->GetPaletteIndex(), stop->GetAlpha(aState)));
    627  }
    628 
    629  // Retrieve the color stops into an array of GradientStop records. The stops
    630  // are normalized to the range [0 .. 1], and the original offsets of the
    631  // first and last stops are returned.
    632  // If aReverse is true, the color line is reversed.
    633  void CollectGradientStops(const PaintState& aState,
    634                            nsTArray<GradientStop>& aStops, float* aFirstStop,
    635                            float* aLastStop, bool aReverse = false) const {
    636    MOZ_ASSERT(aStops.IsEmpty());
    637    uint16_t count = numStops;
    638    if (!count) {
    639      return;
    640    }
    641    const auto* stop = colorStops();
    642    if (reinterpret_cast<const char*>(stop) + count * sizeof(T) >
    643        aState.COLRv1BaseAddr() + aState.mCOLRLength) {
    644      return;
    645    }
    646    aStops.SetCapacity(count);
    647    for (uint16_t i = 0; i < count; ++i, ++stop) {
    648      DeviceColor color =
    649          aState.GetColor(stop->GetPaletteIndex(), stop->GetAlpha(aState));
    650      aStops.AppendElement(GradientStop{stop->GetStopOffset(aState), color});
    651    }
    652    if (count == 1) {
    653      *aFirstStop = *aLastStop = aStops[0].offset;
    654      return;
    655    }
    656    aStops.StableSort();
    657    if (aReverse) {
    658      float a = aStops[0].offset;
    659      float b = aStops.LastElement().offset;
    660      aStops.Reverse();
    661      for (auto& gs : aStops) {
    662        gs.offset = a + b - gs.offset;
    663      }
    664    }
    665    // Normalize stops to the range 0.0 .. 1.0, and return the original
    666    // start & end.
    667    // Note that if all stops are at the same offset, no normalization
    668    // will be done.
    669    *aFirstStop = aStops[0].offset;
    670    *aLastStop = aStops.LastElement().offset;
    671    if ((*aLastStop > *aFirstStop) &&
    672        (*aLastStop != 1.0f || *aFirstStop != 0.0f)) {
    673      float f = 1.0f / (*aLastStop - *aFirstStop);
    674      for (auto& gs : aStops) {
    675        gs.offset = (gs.offset - *aFirstStop) * f;
    676      }
    677    }
    678  }
    679 
    680  // Create a gfx::GradientStops representing the given color line stops,
    681  // applying our extend mode.
    682  already_AddRefed<GradientStops> MakeGradientStops(
    683      const PaintState& aState, nsTArray<GradientStop>& aStops) const {
    684    auto mapExtendMode = [](uint8_t aExtend) -> ExtendMode {
    685      switch (aExtend) {
    686        case EXTEND_REPEAT:
    687          return ExtendMode::REPEAT;
    688        case EXTEND_REFLECT:
    689          return ExtendMode::REFLECT;
    690        case EXTEND_PAD:
    691        default:
    692          return ExtendMode::CLAMP;
    693      }
    694    };
    695    return aState.mDrawTarget->CreateGradientStops(
    696        aStops.Elements(), aStops.Length(), mapExtendMode(extend));
    697  }
    698 
    699  already_AddRefed<GradientStops> MakeGradientStops(
    700      const PaintState& aState, float* aFirstStop, float* aLastStop,
    701      bool aReverse = false) const {
    702    AutoTArray<GradientStop, 8> stops;
    703    CollectGradientStops(aState, stops, aFirstStop, aLastStop, aReverse);
    704    if (stops.IsEmpty()) {
    705      return nullptr;
    706    }
    707    return MakeGradientStops(aState, stops);
    708  }
    709 };
    710 
    711 using ColorLine = ColorLineT<ColorStop>;
    712 using VarColorLine = ColorLineT<VarColorStop>;
    713 
    714 // Used to check for cycles in the paint graph, and bail out to avoid infinite
    715 // recursion when traversing the graph in Paint() or GetBoundingRect(). (Only
    716 // PaintColrLayers and PaintColrGlyph can cause cycles; all other paint types
    717 // have only forward references within the table.)
    718 #define IF_CYCLE_RETURN(retval)             \
    719  if (aState.mVisited->Contains(aOffset)) { \
    720    return retval;                          \
    721  }                                         \
    722  aState.mVisited->AppendElement(aOffset);  \
    723  ScopeExit e([aState]() { aState.mVisited->RemoveLastElement(); })
    724 
    725 struct PaintColrLayers {
    726  enum { kFormat = 1 };
    727  uint8_t format;
    728  uint8_t numLayers;
    729  uint32 firstLayerIndex;
    730 
    731  bool Paint(const PaintState& aState, uint32_t aOffset,
    732             const Rect* aBounds) const {
    733    MOZ_ASSERT(format == kFormat);
    734    IF_CYCLE_RETURN(true);
    735    const auto* layerList = aState.mHeader.v1->layerList();
    736    if (!layerList) {
    737      return false;
    738    }
    739    if (uint64_t(firstLayerIndex) + numLayers > layerList->numLayers) {
    740      return false;
    741    }
    742    const auto* paintOffsets = layerList->paintOffsets() + firstLayerIndex;
    743    for (uint32_t i = 0; i < numLayers; i++) {
    744      if (!DispatchPaint(aState,
    745                         aState.mHeader.v1->layerListOffset + paintOffsets[i],
    746                         aBounds)) {
    747        return false;
    748      }
    749    }
    750    return true;
    751  }
    752 
    753  Rect GetBoundingRect(const PaintState& aState, uint32_t aOffset) const {
    754    MOZ_ASSERT(format == kFormat);
    755    IF_CYCLE_RETURN(Rect());
    756    const auto* layerList = aState.mHeader.v1->layerList();
    757    if (!layerList) {
    758      return Rect();
    759    }
    760    if (uint64_t(firstLayerIndex) + numLayers > layerList->numLayers) {
    761      return Rect();
    762    }
    763    Rect result;
    764    const auto* paintOffsets = layerList->paintOffsets() + firstLayerIndex;
    765    for (uint32_t i = 0; i < numLayers; i++) {
    766      result = result.Union(DispatchGetBounds(
    767          aState, aState.mHeader.v1->layerListOffset + paintOffsets[i]));
    768    }
    769    return result;
    770  }
    771 };
    772 
    773 struct PaintPatternBase {
    774  bool Paint(const PaintState& aState, uint32_t aOffset,
    775             const Rect* aBounds) const {
    776    Matrix m = aState.mDrawTarget->GetTransform();
    777    if (m.Invert()) {
    778      if (auto pattern = DispatchMakePattern(aState, aOffset)) {
    779        aState.mDrawTarget->FillRect(
    780            m.TransformBounds(IntRectToRect(aState.mDrawTarget->GetRect())),
    781            *pattern, aState.mDrawOptions);
    782        return true;
    783      }
    784    }
    785    return false;
    786  }
    787 
    788  Rect GetBoundingRect(const PaintState& aState, uint32_t aOffset) const {
    789    return Rect();
    790  }
    791 };
    792 
    793 struct PaintSolid : public PaintPatternBase {
    794  enum { kFormat = 2 };
    795  uint8_t format;
    796  uint16 paletteIndex;
    797  F2DOT14 alpha;
    798 
    799  UniquePtr<Pattern> MakePattern(const PaintState& aState,
    800                                 uint32_t aOffset) const {
    801    MOZ_ASSERT(format == kFormat);
    802    return MakeUnique<ColorPattern>(aState.GetColor(paletteIndex, alpha));
    803  }
    804 };
    805 
    806 struct PaintVarSolid : public PaintSolid {
    807  enum { kFormat = 3 };
    808  uint32 varIndexBase;
    809 
    810  UniquePtr<Pattern> MakePattern(const PaintState& aState,
    811                                 uint32_t aOffset) const {
    812    MOZ_ASSERT(format == kFormat);
    813    return MakeUnique<ColorPattern>(aState.GetColor(
    814        paletteIndex, ApplyVariation(aState, alpha, varIndexBase)));
    815  }
    816 };
    817 
    818 struct PaintLinearGradient : public PaintPatternBase {
    819  enum { kFormat = 4 };
    820  uint8_t format;
    821  Offset24 colorLineOffset;
    822  FWORD x0;
    823  FWORD y0;
    824  FWORD x1;
    825  FWORD y1;
    826  FWORD x2;
    827  FWORD y2;
    828 
    829  UniquePtr<Pattern> MakePattern(const PaintState& aState,
    830                                 uint32_t aOffset) const {
    831    MOZ_ASSERT(format == kFormat);
    832    uint32_t clOffset = aOffset + colorLineOffset;
    833    if (clOffset + sizeof(ColorLine) + sizeof(ColorStop) > aState.mCOLRLength) {
    834      return nullptr;
    835    }
    836    const auto* colorLine =
    837        reinterpret_cast<const ColorLine*>(aState.COLRv1BaseAddr() + clOffset);
    838    Point p0(aState.F2P(int16_t(x0)), aState.F2P(int16_t(y0)));
    839    Point p1(aState.F2P(int16_t(x1)), aState.F2P(int16_t(y1)));
    840    Point p2(aState.F2P(int16_t(x2)), aState.F2P(int16_t(y2)));
    841    return NormalizeAndMakeGradient(aState, colorLine, p0, p1, p2);
    842  }
    843 
    844  template <typename T>
    845  UniquePtr<Pattern> NormalizeAndMakeGradient(const PaintState& aState,
    846                                              const T* aColorLine, Point p0,
    847                                              Point p1, Point p2) const {
    848    // Ill-formed gradient should not be rendered.
    849    if (p1 == p0 || p2 == p0) {
    850      return MakeUnique<ColorPattern>(DeviceColor());
    851    }
    852    UniquePtr<Pattern> solidColor = aColorLine->AsSolidColor(aState);
    853    if (solidColor) {
    854      return solidColor;
    855    }
    856    float firstStop, lastStop;
    857    AutoTArray<GradientStop, 8> stopArray;
    858    aColorLine->CollectGradientStops(aState, stopArray, &firstStop, &lastStop);
    859    if (stopArray.IsEmpty()) {
    860      return MakeUnique<ColorPattern>(DeviceColor());
    861    }
    862    if (firstStop != 0.0 || lastStop != 1.0) {
    863      if (firstStop == lastStop) {
    864        if (aColorLine->extend != T::EXTEND_PAD) {
    865          return MakeUnique<ColorPattern>(DeviceColor());
    866        }
    867        // For extend-pad, when the color line is zero-length, we add a "fake"
    868        // color stop to create a [0.0..1.0]-normalized color line, so that the
    869        // projection of points below works as expected.
    870        for (auto& gs : stopArray) {
    871          gs.offset = 0.0f;
    872        }
    873        stopArray.AppendElement(
    874            GradientStop{1.0f, stopArray.LastElement().color});
    875        lastStop += 1.0f;
    876      }
    877      // Adjust positions of the points to account for normalization of the
    878      // color line stop offsets.
    879      Point v = p1 - p0;
    880      p0 += v * firstStop;
    881      p1 -= v * (1.0f - lastStop);
    882      // Move the rotation vector to maintain the same direction from p0.
    883      p2 += v * firstStop;
    884    }
    885    Point p3;
    886    if (FuzzyEqualsMultiplicative(p2.y, p0.y)) {
    887      // rotation vector is horizontal
    888      p3 = Point(p0.x, p1.y);
    889    } else if (FuzzyEqualsMultiplicative(p2.x, p0.x)) {
    890      // rotation vector is vertical
    891      p3 = Point(p1.x, p0.y);
    892    } else {
    893      float m = (p2.y - p0.y) / (p2.x - p0.x);  // slope of line p0->p2
    894      float mInv = -1.0f / m;         // slope of desired perpendicular p0->p3
    895      float c1 = p0.y - mInv * p0.x;  // line p0->p3 is m * x - y + c1 = 0
    896      float c2 = p1.y - m * p1.x;     // line p1->p3 is mInv * x - y + c2 = 0
    897      float x3 = (c1 - c2) / (m - mInv);
    898      float y3 = (c1 * m - c2 * mInv) / (m - mInv);
    899      p3 = Point(x3, y3);
    900    }
    901    RefPtr stops = aColorLine->MakeGradientStops(aState, stopArray);
    902    return MakeUnique<LinearGradientPattern>(p0, p3, std::move(stops),
    903                                             Matrix::Scaling(1.0, -1.0));
    904  }
    905 };
    906 
    907 struct PaintVarLinearGradient : public PaintLinearGradient {
    908  enum { kFormat = 5 };
    909  uint32 varIndexBase;
    910 
    911  UniquePtr<Pattern> MakePattern(const PaintState& aState,
    912                                 uint32_t aOffset) const {
    913    MOZ_ASSERT(format == kFormat);
    914    uint32_t clOffset = aOffset + colorLineOffset;
    915    if (clOffset + sizeof(VarColorLine) + sizeof(VarColorStop) >
    916        aState.mCOLRLength) {
    917      return nullptr;
    918    }
    919    const auto* colorLine = reinterpret_cast<const VarColorLine*>(
    920        aState.COLRv1BaseAddr() + clOffset);
    921    Point p0(aState.F2P(ApplyVariation(aState, int16_t(x0), varIndexBase)),
    922             aState.F2P(
    923                 ApplyVariation(aState, int16_t(y0), SatAdd(varIndexBase, 1))));
    924    Point p1(aState.F2P(
    925                 ApplyVariation(aState, int16_t(x1), SatAdd(varIndexBase, 2))),
    926             aState.F2P(
    927                 ApplyVariation(aState, int16_t(y1), SatAdd(varIndexBase, 3))));
    928    Point p2(aState.F2P(
    929                 ApplyVariation(aState, int16_t(x2), SatAdd(varIndexBase, 4))),
    930             aState.F2P(
    931                 ApplyVariation(aState, int16_t(y2), SatAdd(varIndexBase, 5))));
    932    return NormalizeAndMakeGradient(aState, colorLine, p0, p1, p2);
    933  }
    934 };
    935 
    936 struct PaintRadialGradient : public PaintPatternBase {
    937  enum { kFormat = 6 };
    938  uint8_t format;
    939  Offset24 colorLineOffset;
    940  FWORD x0;
    941  FWORD y0;
    942  UFWORD radius0;
    943  FWORD x1;
    944  FWORD y1;
    945  UFWORD radius1;
    946 
    947  UniquePtr<Pattern> MakePattern(const PaintState& aState,
    948                                 uint32_t aOffset) const {
    949    MOZ_ASSERT(format == kFormat);
    950    uint32_t clOffset = aOffset + colorLineOffset;
    951    if (clOffset + sizeof(ColorLine) + sizeof(ColorStop) > aState.mCOLRLength) {
    952      return nullptr;
    953    }
    954    const auto* colorLine =
    955        reinterpret_cast<const ColorLine*>(aState.COLRv1BaseAddr() + clOffset);
    956    Point c1(aState.F2P(int16_t(x0)), aState.F2P(int16_t(y0)));
    957    Point c2(aState.F2P(int16_t(x1)), aState.F2P(int16_t(y1)));
    958    float r1 = aState.F2P(uint16_t(radius0));
    959    float r2 = aState.F2P(uint16_t(radius1));
    960    return NormalizeAndMakeGradient(aState, colorLine, c1, c2, r1, r2);
    961  }
    962 
    963  // Helper function to trim the gradient stops array at the start or end.
    964  void TruncateGradientStops(nsTArray<GradientStop>& aStops, float aStart,
    965                             float aEnd) const {
    966    // For pad mode, we may need a sub-range of the line: figure out which
    967    // stops to trim, and interpolate as needed at truncation point(s).
    968    // (Currently this is only ever used to trim one end of the color line,
    969    // so edge cases that may occur when trimming both ends are untested.)
    970    MOZ_ASSERT(aStart == 0.0f || aEnd == 1.0f,
    971               "Trimming both ends of color-line is untested!");
    972 
    973    // Create a color that is |r| of the way from c1 to c2.
    974    auto interpolateColor = [](DeviceColor c1, DeviceColor c2, float r) {
    975      return DeviceColor(
    976          c2.r * r + c1.r * (1.0f - r), c2.g * r + c1.g * (1.0f - r),
    977          c2.b * r + c1.b * (1.0f - r), c2.a * r + c1.a * (1.0f - r));
    978    };
    979 
    980    size_t count = aStops.Length();
    981    MOZ_ASSERT(count > 1);
    982 
    983    // Truncate at the start of the color line?
    984    if (aStart > 0.0f) {
    985      // Skip forward past any stops that can be dropped.
    986      size_t i = 0;
    987      while (i < count - 1 && aStops[i].offset < aStart) {
    988        ++i;
    989      }
    990      // If we're not truncating exactly at a color-stop offset, shift the
    991      // preceding stop to the truncation offset and interpolate its color.
    992      if (i && aStops[i].offset > aStart) {
    993        auto& prev = aStops[i - 1];
    994        auto& curr = aStops[i];
    995        float ratio = (aStart - prev.offset) / (curr.offset - prev.offset);
    996        prev.color = interpolateColor(prev.color, curr.color, ratio);
    997        prev.offset = aStart;
    998        --i;  // We don't want to remove this stop, as we adjusted it.
    999      }
   1000      aStops.RemoveElementsAt(0, i);
   1001      // Re-normalize the remaining stops to the [0, 1] range.
   1002      if (aStart < 1.0f) {
   1003        float r = 1.0f / (1.0f - aStart);
   1004        for (auto& gs : aStops) {
   1005          gs.offset = r * (gs.offset - aStart);
   1006        }
   1007      }
   1008    }
   1009 
   1010    // Truncate at the end of the color line?
   1011    if (aEnd < 1.0f) {
   1012      // Skip back over any stops that can be dropped.
   1013      size_t i = count - 1;
   1014      while (i && aStops[i].offset > aEnd) {
   1015        --i;
   1016      }
   1017      // If we're not truncating exactly at a color-stop offset, shift the
   1018      // following stop to the truncation offset and interpolate its color.
   1019      if (i + 1 < count && aStops[i].offset < aEnd) {
   1020        auto& next = aStops[i + 1];
   1021        auto& curr = aStops[i];
   1022        float ratio = (aEnd - curr.offset) / (next.offset - curr.offset);
   1023        next.color = interpolateColor(curr.color, next.color, ratio);
   1024        next.offset = aEnd;
   1025        ++i;
   1026      }
   1027      aStops.RemoveElementsAt(i + 1, count - i - 1);
   1028      // Re-normalize the remaining stops to the [0, 1] range.
   1029      if (aEnd > 0.0f) {
   1030        float r = 1.0f / aEnd;
   1031        for (auto& gs : aStops) {
   1032          gs.offset = r * gs.offset;
   1033        }
   1034      }
   1035    }
   1036  }
   1037 
   1038  template <typename T>
   1039  UniquePtr<Pattern> NormalizeAndMakeGradient(const PaintState& aState,
   1040                                              const T* aColorLine, Point c1,
   1041                                              Point c2, float r1,
   1042                                              float r2) const {
   1043    if ((c1 == c2 && r1 == r2) || (r1 == 0.0 && r2 == 0.0)) {
   1044      return MakeUnique<ColorPattern>(DeviceColor());
   1045    }
   1046    UniquePtr<Pattern> solidColor = aColorLine->AsSolidColor(aState);
   1047    if (solidColor) {
   1048      return solidColor;
   1049    }
   1050    float firstStop, lastStop;
   1051    AutoTArray<GradientStop, 8> stopArray;
   1052    aColorLine->CollectGradientStops(aState, stopArray, &firstStop, &lastStop);
   1053    if (stopArray.IsEmpty()) {
   1054      return MakeUnique<ColorPattern>(DeviceColor());
   1055    }
   1056    // If the color stop offsets had to be normalized to the [0, 1] range,
   1057    // adjust the circle positions and radii to match.
   1058    if (firstStop != 0.0f || lastStop != 1.0f) {
   1059      if (firstStop == lastStop) {
   1060        if (aColorLine->extend != T::EXTEND_PAD) {
   1061          return MakeUnique<ColorPattern>(DeviceColor());
   1062        }
   1063        // For extend-pad, when the color line is zero-length, we add a "fake"
   1064        // color stop to ensure we'll maintain the orientation of the cone,
   1065        // otherwise when we adjust circles to account for the normalized color
   1066        // stops, the centers will coalesce and the cone or cylinder collapses.
   1067        for (auto& gs : stopArray) {
   1068          gs.offset = 0.0f;
   1069        }
   1070        stopArray.AppendElement(
   1071            GradientStop{1.0f, stopArray.LastElement().color});
   1072        lastStop += 1.0f;
   1073      }
   1074      // Adjust centers along the vector between them, and scale radii for
   1075      // gradient line defined from 0.0 to 1.0.
   1076      Point vec = c2 - c1;
   1077      c1 += vec * firstStop;
   1078      c2 -= vec * (1.0f - lastStop);
   1079      float deltaR = r2 - r1;
   1080      r1 = r1 + deltaR * firstStop;
   1081      r2 = r2 - deltaR * (1.0f - lastStop);
   1082    }
   1083    if ((r1 < 0.0f || r2 < 0.0f) && aColorLine->extend == T::EXTEND_PAD) {
   1084      // For EXTEND_PAD, we can restrict the gradient definition to just its
   1085      // visible portion because the shader doesn't need to see any part of the
   1086      // color line that extends into the negative-radius "virtual cone".
   1087      if (r1 < 0.0f && r2 < 0.0f) {
   1088        // If both radii are negative, then only the color at the closer circle
   1089        // will appear in the projected positive cone (or if they're equal,
   1090        // nothing will be visible at all).
   1091        if (r1 == r2) {
   1092          return MakeUnique<ColorPattern>(DeviceColor());
   1093        }
   1094        // The defined range of the color line is entirely in the invisible
   1095        // cone; all that will project into visible space is a single color.
   1096        if (r1 < r2) {
   1097          // Keep only the last color stop.
   1098          stopArray.RemoveElementsAt(0, stopArray.Length() - 1);
   1099        } else {
   1100          // Keep only the first color stop.
   1101          stopArray.RemoveElementsAt(1, stopArray.Length() - 1);
   1102        }
   1103      } else {
   1104        // Truncate the gradient at the tip of the visible cone: find the color
   1105        // stops closest to that point and interpolate between them.
   1106        if (r1 < r2) {
   1107          float start = r1 / (r1 - r2);
   1108          TruncateGradientStops(stopArray, start, 1.0f);
   1109          r1 = 0.0f;
   1110          c1 = c1 * (1.0f - start) + c2 * start;
   1111        } else if (r2 < r1) {
   1112          float end = 1.0f - r2 / (r2 - r1);
   1113          TruncateGradientStops(stopArray, 0.0f, end);
   1114          r2 = 0.0f;
   1115          c2 = c1 * (1.0f - end) + c2 * end;
   1116        }
   1117      }
   1118    }
   1119    // Handle negative radii, which the shader won't understand directly, by
   1120    // projecting the circles along the cones such that both radii are positive.
   1121    if (r1 < 0.0f || r2 < 0.0f) {
   1122      float deltaR = r2 - r1;
   1123      // If deltaR is zero, then nothing is visible because the cone has
   1124      // degenerated into a negative-radius cylinder, and does not project
   1125      // into visible space at all.
   1126      if (deltaR == 0.0f) {
   1127        return MakeUnique<ColorPattern>(DeviceColor());
   1128      }
   1129      Point vec = c2 - c1;
   1130      if (aColorLine->extend == T::EXTEND_REFLECT) {
   1131        deltaR *= 2.0f;
   1132        vec = vec * 2.0f;
   1133      }
   1134      if (r2 < r1) {
   1135        vec = -vec;
   1136        deltaR = -deltaR;
   1137      }
   1138      // Number of repeats by which we need to shift.
   1139      float n = std::ceil(std::max(-r1, -r2) / deltaR);
   1140      deltaR *= n;
   1141      r1 += deltaR;
   1142      r2 += deltaR;
   1143      vec = vec * n;
   1144      c1 += vec;
   1145      c2 += vec;
   1146    }
   1147    RefPtr stops = aColorLine->MakeGradientStops(aState, stopArray);
   1148    if (!stops) {
   1149      return MakeUnique<ColorPattern>(DeviceColor());
   1150    }
   1151    return MakeUnique<RadialGradientPattern>(c1, c2, r1, r2, std::move(stops),
   1152                                             Matrix::Scaling(1.0, -1.0));
   1153  }
   1154 };
   1155 
   1156 struct PaintVarRadialGradient : public PaintRadialGradient {
   1157  enum { kFormat = 7 };
   1158  uint32 varIndexBase;
   1159 
   1160  UniquePtr<Pattern> MakePattern(const PaintState& aState,
   1161                                 uint32_t aOffset) const {
   1162    MOZ_ASSERT(format == kFormat);
   1163    uint32_t clOffset = aOffset + colorLineOffset;
   1164    if (clOffset + sizeof(VarColorLine) + sizeof(VarColorStop) >
   1165        aState.mCOLRLength) {
   1166      return nullptr;
   1167    }
   1168    const auto* colorLine = reinterpret_cast<const VarColorLine*>(
   1169        aState.COLRv1BaseAddr() + clOffset);
   1170    Point c1(aState.F2P(ApplyVariation(aState, int16_t(x0), varIndexBase)),
   1171             aState.F2P(
   1172                 ApplyVariation(aState, int16_t(y0), SatAdd(varIndexBase, 1))));
   1173    float r1 = aState.F2P(
   1174        ApplyVariation(aState, uint16_t(radius0), SatAdd(varIndexBase, 2)));
   1175    Point c2(aState.F2P(
   1176                 ApplyVariation(aState, int16_t(x1), SatAdd(varIndexBase, 3))),
   1177             aState.F2P(
   1178                 ApplyVariation(aState, int16_t(y1), SatAdd(varIndexBase, 4))));
   1179    float r2 = aState.F2P(
   1180        ApplyVariation(aState, uint16_t(radius1), SatAdd(varIndexBase, 5)));
   1181    return NormalizeAndMakeGradient(aState, colorLine, c1, c2, r1, r2);
   1182  }
   1183 };
   1184 
   1185 struct PaintSweepGradient : public PaintPatternBase {
   1186  enum { kFormat = 8 };
   1187  uint8_t format;
   1188  Offset24 colorLineOffset;
   1189  FWORD centerX;
   1190  FWORD centerY;
   1191  F2DOT14 startAngle;
   1192  F2DOT14 endAngle;
   1193 
   1194  UniquePtr<Pattern> MakePattern(const PaintState& aState,
   1195                                 uint32_t aOffset) const {
   1196    MOZ_ASSERT(format == kFormat);
   1197    uint32_t clOffset = aOffset + colorLineOffset;
   1198    if (clOffset + sizeof(ColorLine) + sizeof(ColorStop) > aState.mCOLRLength) {
   1199      return nullptr;
   1200    }
   1201    const auto* colorLine =
   1202        reinterpret_cast<const ColorLine*>(aState.COLRv1BaseAddr() + clOffset);
   1203    float start = float(startAngle) + 1.0f;
   1204    float end = float(endAngle) + 1.0f;
   1205    Point center(aState.F2P(int16_t(centerX)), aState.F2P(int16_t(centerY)));
   1206    return NormalizeAndMakeGradient(aState, colorLine, center, start, end);
   1207  }
   1208 
   1209  template <typename T>
   1210  UniquePtr<Pattern> NormalizeAndMakeGradient(const PaintState& aState,
   1211                                              const T* aColorLine,
   1212                                              Point aCenter, float aStart,
   1213                                              float aEnd) const {
   1214    if (aStart == aEnd && aColorLine->extend != T::EXTEND_PAD) {
   1215      return MakeUnique<ColorPattern>(DeviceColor());
   1216    }
   1217    UniquePtr<Pattern> solidColor = aColorLine->AsSolidColor(aState);
   1218    if (solidColor) {
   1219      return solidColor;
   1220    }
   1221    // ConicGradientPattern works counterclockwise. If the gradient is defined
   1222    // clockwise (with aStart greater than aEnd), we'll reverse the color line
   1223    // and swap the start and end angles.
   1224    bool reverse = aEnd < aStart;
   1225    float firstStop, lastStop;
   1226    RefPtr stops =
   1227        aColorLine->MakeGradientStops(aState, &firstStop, &lastStop, reverse);
   1228    if (!stops) {
   1229      return nullptr;
   1230    }
   1231    if (firstStop != 0.0 || lastStop != 1.0) {
   1232      if (firstStop == lastStop) {
   1233        if (aColorLine->extend != T::EXTEND_PAD) {
   1234          return MakeUnique<ColorPattern>(DeviceColor());
   1235        }
   1236      } else {
   1237        float sweep = aEnd - aStart;
   1238        aStart = aStart + sweep * firstStop;
   1239        aEnd = aStart + sweep * (lastStop - firstStop);
   1240      }
   1241    }
   1242    if (reverse) {
   1243      std::swap(aStart, aEnd);
   1244    }
   1245    return MakeUnique<ConicGradientPattern>(aCenter, M_PI / 2.0, aStart / 2.0,
   1246                                            aEnd / 2.0, std::move(stops),
   1247                                            Matrix::Scaling(1.0, -1.0));
   1248  }
   1249 };
   1250 
   1251 struct PaintVarSweepGradient : public PaintSweepGradient {
   1252  enum { kFormat = 9 };
   1253  uint32 varIndexBase;
   1254 
   1255  UniquePtr<Pattern> MakePattern(const PaintState& aState,
   1256                                 uint32_t aOffset) const {
   1257    MOZ_ASSERT(format == kFormat);
   1258    uint32_t clOffset = aOffset + colorLineOffset;
   1259    if (clOffset + sizeof(VarColorLine) + sizeof(VarColorStop) >
   1260        aState.mCOLRLength) {
   1261      return nullptr;
   1262    }
   1263    const auto* colorLine = reinterpret_cast<const VarColorLine*>(
   1264        aState.COLRv1BaseAddr() + clOffset);
   1265    float start =
   1266        ApplyVariation(aState, startAngle, SatAdd(varIndexBase, 2)) + 1.0f;
   1267    float end =
   1268        ApplyVariation(aState, endAngle, SatAdd(varIndexBase, 3)) + 1.0f;
   1269    Point center(
   1270        aState.F2P(ApplyVariation(aState, int16_t(centerX), varIndexBase)),
   1271        aState.F2P(
   1272            ApplyVariation(aState, int16_t(centerY), SatAdd(varIndexBase, 1))));
   1273    return NormalizeAndMakeGradient(aState, colorLine, center, start, end);
   1274  }
   1275 };
   1276 
   1277 struct PaintGlyph {
   1278  enum { kFormat = 10 };
   1279  uint8_t format;
   1280  Offset24 paintOffset;
   1281  uint16 glyphID;
   1282 
   1283  bool Paint(const PaintState& aState, uint32_t aOffset,
   1284             const Rect* aBounds) const {
   1285    MOZ_ASSERT(format == kFormat);
   1286    if (!paintOffset) {
   1287      return true;
   1288    }
   1289    Glyph g{uint16_t(glyphID), Point()};
   1290    GlyphBuffer buffer{&g, 1};
   1291    // If the paint is a simple fill (rather than a sub-graph of further paint
   1292    // records), we can just use FillGlyphs to render it instead of setting up
   1293    // a clip.
   1294    UniquePtr<Pattern> fillPattern =
   1295        DispatchMakePattern(aState, aOffset + paintOffset);
   1296    if (fillPattern) {
   1297      // On macOS we can't use FillGlyphs because when we render the glyph,
   1298      // Core Text's own color font support may step in and ignore the
   1299      // pattern. So to avoid this, fill the glyph as a path instead.
   1300 #if XP_MACOSX
   1301      RefPtr<Path> path = GetPathForGlyphs(aState, buffer);
   1302      aState.mDrawTarget->Fill(path, *fillPattern, aState.mDrawOptions);
   1303 #else
   1304      aState.mDrawTarget->FillGlyphs(aState.mScaledFont, buffer, *fillPattern,
   1305                                     aState.mDrawOptions);
   1306 #endif
   1307      return true;
   1308    }
   1309    RefPtr<Path> path = GetPathForGlyphs(aState, buffer);
   1310    aState.mDrawTarget->PushClip(path);
   1311    bool ok = DispatchPaint(aState, aOffset + paintOffset, aBounds);
   1312    aState.mDrawTarget->PopClip();
   1313    return ok;
   1314  }
   1315 
   1316  Rect GetBoundingRect(const PaintState& aState, uint32_t aOffset) const {
   1317    MOZ_ASSERT(format == kFormat);
   1318    Glyph g{uint16_t(glyphID), Point()};
   1319    GlyphBuffer buffer{&g, 1};
   1320    RefPtr<Path> path = GetPathForGlyphs(aState, buffer);
   1321    return path->GetFastBounds();
   1322  }
   1323 
   1324 private:
   1325  RefPtr<Path> GetPathForGlyphs(const PaintState& aState,
   1326                                const GlyphBuffer& buffer) const {
   1327    if (aState.mDrawTarget->GetBackendType() == BackendType::WEBRENDER_TEXT) {
   1328      RefPtr dt = gfxPlatform::ThreadLocalScreenReferenceDrawTarget();
   1329      return aState.mScaledFont->GetPathForGlyphs(buffer, dt);
   1330    }
   1331    return aState.mScaledFont->GetPathForGlyphs(buffer, aState.mDrawTarget);
   1332  }
   1333 };
   1334 
   1335 struct PaintColrGlyph {
   1336  enum { kFormat = 11 };
   1337  uint8_t format;
   1338  uint16 glyphID;
   1339 
   1340  // Factored out as a helper because this is also used by the top-level
   1341  // PaintGlyphGraph function.
   1342  static bool DoPaint(const PaintState& aState,
   1343                      const BaseGlyphPaintRecord* aBaseGlyphPaint,
   1344                      uint32_t aGlyphId, const Rect* aBounds) {
   1345    AutoPopClips clips(aState.mDrawTarget);
   1346    Rect clipRect;
   1347    if (const auto* clipList = aState.mHeader.v1->clipList()) {
   1348      if (const auto* clip = clipList->GetClip(aGlyphId)) {
   1349        clipRect = clip->GetRect(aState);
   1350        clips.PushClipRect(clipRect);
   1351        if (!aBounds) {
   1352          aBounds = &clipRect;
   1353        }
   1354      }
   1355    }
   1356    return DispatchPaint(
   1357        aState,
   1358        aState.mHeader.v1->baseGlyphListOffset + aBaseGlyphPaint->paintOffset,
   1359        aBounds);
   1360  }
   1361 
   1362  bool Paint(const PaintState& aState, uint32_t aOffset,
   1363             const Rect* aBounds) const {
   1364    MOZ_ASSERT(format == kFormat);
   1365    IF_CYCLE_RETURN(true);
   1366    const auto* base = aState.mHeader.v1->GetBaseGlyphPaint(glyphID);
   1367    return base ? DoPaint(aState, base, uint16_t(glyphID), aBounds) : false;
   1368  }
   1369 
   1370  Rect GetBoundingRect(const PaintState& aState, uint32_t aOffset) const {
   1371    IF_CYCLE_RETURN(Rect());
   1372    if (const auto* clipList = aState.mHeader.v1->clipList()) {
   1373      if (const auto* clip = clipList->GetClip(uint16_t(glyphID))) {
   1374        return clip->GetRect(aState);
   1375      }
   1376    }
   1377    if (const auto* base =
   1378            aState.mHeader.v1->GetBaseGlyphPaint(uint16_t(glyphID))) {
   1379      return DispatchGetBounds(
   1380          aState, aState.mHeader.v1->baseGlyphListOffset + base->paintOffset);
   1381    }
   1382    return Rect();
   1383  }
   1384 };
   1385 
   1386 #undef IF_CYCLE_RETURN
   1387 
   1388 struct Affine2x3 {
   1389  Fixed xx;
   1390  Fixed yx;
   1391  Fixed xy;
   1392  Fixed yy;
   1393  Fixed dx;
   1394  Fixed dy;
   1395 
   1396  Matrix AsMatrix(const PaintState& aState) const {
   1397    // Flip signs because of opposite y-axis direction in moz2d vs opentype.
   1398    return Matrix(float(xx), -float(yx), -float(xy), float(yy),
   1399                  aState.F2P(float(dx)), -aState.F2P(float(dy)));
   1400  }
   1401 };
   1402 
   1403 struct VarAffine2x3 : public Affine2x3 {
   1404  uint32 varIndexBase;
   1405 
   1406  Matrix AsMatrix(const PaintState& aState) const {
   1407    return Matrix(
   1408        ApplyVariation(aState, xx, varIndexBase),
   1409        -ApplyVariation(aState, yx, SatAdd(varIndexBase, 1)),
   1410        -ApplyVariation(aState, xy, SatAdd(varIndexBase, 2)),
   1411        ApplyVariation(aState, yy, SatAdd(varIndexBase, 3)),
   1412        aState.F2P(ApplyVariation(aState, dx, SatAdd(varIndexBase, 4))),
   1413        -aState.F2P(ApplyVariation(aState, dy, SatAdd(varIndexBase, 5))));
   1414  };
   1415 };
   1416 
   1417 struct PaintTransformBase {
   1418  uint8_t format;
   1419  Offset24 paintOffset;
   1420 
   1421  bool Paint(const PaintState& aState, uint32_t aOffset,
   1422             const Rect* aBounds) const {
   1423    if (!paintOffset) {
   1424      return true;
   1425    }
   1426    AutoRestoreTransform saveTransform(aState.mDrawTarget);
   1427    aState.mDrawTarget->ConcatTransform(DispatchGetMatrix(aState, aOffset));
   1428    return DispatchPaint(aState, aOffset + paintOffset, aBounds);
   1429  }
   1430 
   1431  Rect GetBoundingRect(const PaintState& aState, uint32_t aOffset) const {
   1432    if (!paintOffset) {
   1433      return Rect();
   1434    }
   1435    Rect bounds = DispatchGetBounds(aState, aOffset + paintOffset);
   1436    bounds = DispatchGetMatrix(aState, aOffset).TransformBounds(bounds);
   1437    return bounds;
   1438  }
   1439 };
   1440 
   1441 struct PaintTransform : public PaintTransformBase {
   1442  enum { kFormat = 12 };
   1443  Offset24 transformOffset;
   1444 
   1445  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1446    MOZ_ASSERT(format == kFormat);
   1447    if (aOffset + transformOffset + sizeof(Affine2x3) > aState.mCOLRLength) {
   1448      return Matrix();
   1449    }
   1450    const auto* t = reinterpret_cast<const Affine2x3*>(
   1451        aState.COLRv1BaseAddr() + aOffset + transformOffset);
   1452    return t->AsMatrix(aState);
   1453  }
   1454 };
   1455 
   1456 struct PaintVarTransform : public PaintTransformBase {
   1457  enum { kFormat = 13 };
   1458  Offset24 transformOffset;
   1459 
   1460  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1461    MOZ_ASSERT(format == kFormat);
   1462    if (aOffset + transformOffset + sizeof(VarAffine2x3) > aState.mCOLRLength) {
   1463      return Matrix();
   1464    }
   1465    const auto* t = reinterpret_cast<const VarAffine2x3*>(
   1466        aState.COLRv1BaseAddr() + aOffset + transformOffset);
   1467    return t->AsMatrix(aState);
   1468  }
   1469 };
   1470 
   1471 struct PaintTranslate : public PaintTransformBase {
   1472  enum { kFormat = 14 };
   1473  FWORD dx;
   1474  FWORD dy;
   1475 
   1476  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1477    MOZ_ASSERT(format == kFormat);
   1478    return Matrix::Translation(aState.F2P(int16_t(dx)),
   1479                               -aState.F2P(int16_t(dy)));
   1480  }
   1481 };
   1482 
   1483 struct PaintVarTranslate : public PaintTranslate {
   1484  enum { kFormat = 15 };
   1485  uint32 varIndexBase;
   1486 
   1487  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1488    MOZ_ASSERT(format == kFormat);
   1489    return Matrix::Translation(
   1490        aState.F2P(ApplyVariation(aState, int16_t(dx), varIndexBase)),
   1491        -aState.F2P(
   1492            ApplyVariation(aState, int16_t(dy), SatAdd(varIndexBase, 1))));
   1493  }
   1494 };
   1495 
   1496 struct PaintScale : public PaintTransformBase {
   1497  enum { kFormat = 16 };
   1498  F2DOT14 scaleX;
   1499  F2DOT14 scaleY;
   1500 
   1501  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1502    MOZ_ASSERT(format == kFormat);
   1503    return Matrix::Scaling(float(scaleX), float(scaleY));
   1504  }
   1505 };
   1506 
   1507 struct PaintVarScale : public PaintScale {
   1508  enum { kFormat = 17 };
   1509  uint32 varIndexBase;
   1510 
   1511  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1512    MOZ_ASSERT(format == kFormat);
   1513    return Matrix::Scaling(
   1514        ApplyVariation(aState, scaleX, varIndexBase),
   1515        ApplyVariation(aState, scaleY, SatAdd(varIndexBase, 1)));
   1516  }
   1517 };
   1518 
   1519 struct PaintScaleAroundCenter : public PaintTransformBase {
   1520  enum { kFormat = 18 };
   1521  F2DOT14 scaleX;
   1522  F2DOT14 scaleY;
   1523  FWORD centerX;
   1524  FWORD centerY;
   1525 
   1526  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1527    MOZ_ASSERT(format == kFormat);
   1528    Point center(aState.F2P(int16_t(centerX)), -aState.F2P(int16_t(centerY)));
   1529    return Matrix::Translation(center)
   1530        .PreScale(float(scaleX), float(scaleY))
   1531        .PreTranslate(-center);
   1532  }
   1533 };
   1534 
   1535 struct PaintVarScaleAroundCenter : public PaintScaleAroundCenter {
   1536  enum { kFormat = 19 };
   1537  uint32 varIndexBase;
   1538 
   1539  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1540    MOZ_ASSERT(format == kFormat);
   1541    Point center(aState.F2P(ApplyVariation(aState, int16_t(centerX),
   1542                                           SatAdd(varIndexBase, 2))),
   1543                 -aState.F2P(ApplyVariation(aState, int16_t(centerY),
   1544                                            SatAdd(varIndexBase, 3))));
   1545    return Matrix::Translation(center)
   1546        .PreScale(ApplyVariation(aState, scaleX, varIndexBase),
   1547                  ApplyVariation(aState, scaleY, SatAdd(varIndexBase, 1)))
   1548        .PreTranslate(-center);
   1549  }
   1550 };
   1551 
   1552 struct PaintScaleUniform : public PaintTransformBase {
   1553  enum { kFormat = 20 };
   1554  F2DOT14 scale;
   1555 
   1556  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1557    MOZ_ASSERT(format == kFormat);
   1558    return Matrix::Scaling(float(scale), float(scale));
   1559  }
   1560 };
   1561 
   1562 struct PaintVarScaleUniform : public PaintScaleUniform {
   1563  enum { kFormat = 21 };
   1564  uint32 varIndexBase;
   1565 
   1566  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1567    MOZ_ASSERT(format == kFormat);
   1568    float sc = ApplyVariation(aState, scale, varIndexBase);
   1569    return Matrix::Scaling(sc, sc);
   1570  }
   1571 };
   1572 
   1573 struct PaintScaleUniformAroundCenter : public PaintTransformBase {
   1574  enum { kFormat = 22 };
   1575  F2DOT14 scale;
   1576  FWORD centerX;
   1577  FWORD centerY;
   1578 
   1579  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1580    MOZ_ASSERT(format == kFormat);
   1581    Point center(aState.F2P(int16_t(centerX)), -aState.F2P(int16_t(centerY)));
   1582    return Matrix::Translation(center)
   1583        .PreScale(float(scale), float(scale))
   1584        .PreTranslate(-center);
   1585  }
   1586 };
   1587 
   1588 struct PaintVarScaleUniformAroundCenter : public PaintScaleUniformAroundCenter {
   1589  enum { kFormat = 23 };
   1590  uint32 varIndexBase;
   1591 
   1592  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1593    MOZ_ASSERT(format == kFormat);
   1594    float sc = ApplyVariation(aState, scale, varIndexBase);
   1595    Point center(aState.F2P(ApplyVariation(aState, int16_t(centerX),
   1596                                           SatAdd(varIndexBase, 1))),
   1597                 -aState.F2P(ApplyVariation(aState, int16_t(centerY),
   1598                                            SatAdd(varIndexBase, 2))));
   1599    return Matrix::Translation(center).PreScale(sc, sc).PreTranslate(-center);
   1600  }
   1601 };
   1602 
   1603 struct PaintRotate : public PaintTransformBase {
   1604  enum { kFormat = 24 };
   1605  F2DOT14 angle;
   1606 
   1607  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1608    MOZ_ASSERT(format == kFormat);
   1609    return Matrix::Rotation(-float(angle) * float(M_PI));
   1610  }
   1611 };
   1612 
   1613 struct PaintVarRotate : public PaintRotate {
   1614  enum { kFormat = 25 };
   1615  uint32 varIndexBase;
   1616 
   1617  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1618    MOZ_ASSERT(format == kFormat);
   1619    float ang = ApplyVariation(aState, angle, varIndexBase);
   1620    return Matrix::Rotation(-ang * float(M_PI));
   1621  }
   1622 };
   1623 
   1624 struct PaintRotateAroundCenter : public PaintTransformBase {
   1625  enum { kFormat = 26 };
   1626  F2DOT14 angle;
   1627  FWORD centerX;
   1628  FWORD centerY;
   1629 
   1630  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1631    MOZ_ASSERT(format == kFormat);
   1632    Point center(aState.F2P(int16_t(centerX)), -aState.F2P(int16_t(centerY)));
   1633    return Matrix::Translation(center)
   1634        .PreRotate(-float(angle) * float(M_PI))
   1635        .PreTranslate(-center);
   1636  }
   1637 };
   1638 
   1639 struct PaintVarRotateAroundCenter : public PaintRotateAroundCenter {
   1640  enum { kFormat = 27 };
   1641  uint32 varIndexBase;
   1642 
   1643  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1644    MOZ_ASSERT(format == kFormat);
   1645    float ang = ApplyVariation(aState, angle, varIndexBase);
   1646    Point center(aState.F2P(ApplyVariation(aState, int16_t(centerX),
   1647                                           SatAdd(varIndexBase, 1))),
   1648                 -aState.F2P(ApplyVariation(aState, int16_t(centerY),
   1649                                            SatAdd(varIndexBase, 2))));
   1650    return Matrix::Translation(center)
   1651        .PreRotate(-ang * float(M_PI))
   1652        .PreTranslate(-center);
   1653  }
   1654 };
   1655 
   1656 static inline Matrix SkewMatrix(float aSkewX, float aSkewY) {
   1657  float xy = tanf(aSkewX * float(M_PI));
   1658  float yx = tanf(aSkewY * float(M_PI));
   1659  return std::isnan(xy) || std::isnan(yx) ? Matrix()
   1660                                          : Matrix(1.0, -yx, xy, 1.0, 0.0, 0.0);
   1661 }
   1662 
   1663 struct PaintSkew : public PaintTransformBase {
   1664  enum { kFormat = 28 };
   1665  F2DOT14 xSkewAngle;
   1666  F2DOT14 ySkewAngle;
   1667 
   1668  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1669    MOZ_ASSERT(format == kFormat);
   1670    return SkewMatrix(float(xSkewAngle), float(ySkewAngle));
   1671  }
   1672 };
   1673 
   1674 struct PaintVarSkew : public PaintSkew {
   1675  enum { kFormat = 29 };
   1676  uint32 varIndexBase;
   1677 
   1678  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1679    MOZ_ASSERT(format == kFormat);
   1680    return SkewMatrix(
   1681        float(ApplyVariation(aState, xSkewAngle, varIndexBase)),
   1682        float(ApplyVariation(aState, ySkewAngle, SatAdd(varIndexBase, 1))));
   1683  }
   1684 };
   1685 
   1686 struct PaintSkewAroundCenter : public PaintTransformBase {
   1687  enum { kFormat = 30 };
   1688  F2DOT14 xSkewAngle;
   1689  F2DOT14 ySkewAngle;
   1690  FWORD centerX;
   1691  FWORD centerY;
   1692 
   1693  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1694    MOZ_ASSERT(format == kFormat);
   1695    Point center(aState.F2P(int16_t(centerX)), -aState.F2P(int16_t(centerY)));
   1696    return Matrix::Translation(center)
   1697        .PreMultiply(SkewMatrix(float(xSkewAngle), float(ySkewAngle)))
   1698        .PreTranslate(-center);
   1699  }
   1700 };
   1701 
   1702 struct PaintVarSkewAroundCenter : public PaintSkewAroundCenter {
   1703  enum { kFormat = 31 };
   1704  uint32 varIndexBase;
   1705 
   1706  Matrix GetMatrix(const PaintState& aState, uint32_t aOffset) const {
   1707    MOZ_ASSERT(format == kFormat);
   1708    Point center(aState.F2P(ApplyVariation(aState, int16_t(centerX),
   1709                                           SatAdd(varIndexBase, 2))),
   1710                 -aState.F2P(ApplyVariation(aState, int16_t(centerY),
   1711                                            SatAdd(varIndexBase, 3))));
   1712    return Matrix::Translation(center)
   1713        .PreMultiply(SkewMatrix(
   1714            ApplyVariation(aState, xSkewAngle, varIndexBase),
   1715            ApplyVariation(aState, ySkewAngle, SatAdd(varIndexBase, 1))))
   1716        .PreTranslate(-center);
   1717  }
   1718 };
   1719 
   1720 struct PaintComposite {
   1721  enum { kFormat = 32 };
   1722  uint8_t format;
   1723  Offset24 sourcePaintOffset;
   1724  uint8_t compositeMode;
   1725  Offset24 backdropPaintOffset;
   1726 
   1727  enum {
   1728    COMPOSITE_CLEAR = 0,
   1729    COMPOSITE_SRC = 1,
   1730    COMPOSITE_DEST = 2,
   1731    COMPOSITE_SRC_OVER = 3,
   1732    COMPOSITE_DEST_OVER = 4,
   1733    COMPOSITE_SRC_IN = 5,
   1734    COMPOSITE_DEST_IN = 6,
   1735    COMPOSITE_SRC_OUT = 7,
   1736    COMPOSITE_DEST_OUT = 8,
   1737    COMPOSITE_SRC_ATOP = 9,
   1738    COMPOSITE_DEST_ATOP = 10,
   1739    COMPOSITE_XOR = 11,
   1740    COMPOSITE_PLUS = 12,
   1741    COMPOSITE_SCREEN = 13,
   1742    COMPOSITE_OVERLAY = 14,
   1743    COMPOSITE_DARKEN = 15,
   1744    COMPOSITE_LIGHTEN = 16,
   1745    COMPOSITE_COLOR_DODGE = 17,
   1746    COMPOSITE_COLOR_BURN = 18,
   1747    COMPOSITE_HARD_LIGHT = 19,
   1748    COMPOSITE_SOFT_LIGHT = 20,
   1749    COMPOSITE_DIFFERENCE = 21,
   1750    COMPOSITE_EXCLUSION = 22,
   1751    COMPOSITE_MULTIPLY = 23,
   1752    COMPOSITE_HSL_HUE = 24,
   1753    COMPOSITE_HSL_SATURATION = 25,
   1754    COMPOSITE_HSL_COLOR = 26,
   1755    COMPOSITE_HSL_LUMINOSITY = 27
   1756  };
   1757 
   1758  bool Paint(const PaintState& aState, uint32_t aOffset,
   1759             const Rect* aBounds) const {
   1760    MOZ_ASSERT(format == kFormat);
   1761    if (!backdropPaintOffset || !sourcePaintOffset) {
   1762      return true;
   1763    }
   1764    auto mapCompositionMode = [](uint8_t aMode) -> CompositionOp {
   1765      switch (aMode) {
   1766        default:
   1767          return CompositionOp::OP_SOURCE;
   1768        case COMPOSITE_CLEAR:
   1769        case COMPOSITE_SRC:
   1770        case COMPOSITE_DEST:
   1771          MOZ_ASSERT_UNREACHABLE("should have short-circuited");
   1772          return CompositionOp::OP_SOURCE;
   1773        case COMPOSITE_SRC_OVER:
   1774          return CompositionOp::OP_OVER;
   1775        case COMPOSITE_DEST_OVER:
   1776          return CompositionOp::OP_DEST_OVER;
   1777        case COMPOSITE_SRC_IN:
   1778          return CompositionOp::OP_IN;
   1779        case COMPOSITE_DEST_IN:
   1780          return CompositionOp::OP_DEST_IN;
   1781        case COMPOSITE_SRC_OUT:
   1782          return CompositionOp::OP_OUT;
   1783        case COMPOSITE_DEST_OUT:
   1784          return CompositionOp::OP_DEST_OUT;
   1785        case COMPOSITE_SRC_ATOP:
   1786          return CompositionOp::OP_ATOP;
   1787        case COMPOSITE_DEST_ATOP:
   1788          return CompositionOp::OP_DEST_ATOP;
   1789        case COMPOSITE_XOR:
   1790          return CompositionOp::OP_XOR;
   1791        case COMPOSITE_PLUS:
   1792          return CompositionOp::OP_ADD;
   1793        case COMPOSITE_SCREEN:
   1794          return CompositionOp::OP_SCREEN;
   1795        case COMPOSITE_OVERLAY:
   1796          return CompositionOp::OP_OVERLAY;
   1797        case COMPOSITE_DARKEN:
   1798          return CompositionOp::OP_DARKEN;
   1799        case COMPOSITE_LIGHTEN:
   1800          return CompositionOp::OP_LIGHTEN;
   1801        case COMPOSITE_COLOR_DODGE:
   1802          return CompositionOp::OP_COLOR_DODGE;
   1803        case COMPOSITE_COLOR_BURN:
   1804          return CompositionOp::OP_COLOR_BURN;
   1805        case COMPOSITE_HARD_LIGHT:
   1806          return CompositionOp::OP_HARD_LIGHT;
   1807        case COMPOSITE_SOFT_LIGHT:
   1808          return CompositionOp::OP_SOFT_LIGHT;
   1809        case COMPOSITE_DIFFERENCE:
   1810          return CompositionOp::OP_DIFFERENCE;
   1811        case COMPOSITE_EXCLUSION:
   1812          return CompositionOp::OP_EXCLUSION;
   1813        case COMPOSITE_MULTIPLY:
   1814          return CompositionOp::OP_MULTIPLY;
   1815        case COMPOSITE_HSL_HUE:
   1816          return CompositionOp::OP_HUE;
   1817        case COMPOSITE_HSL_SATURATION:
   1818          return CompositionOp::OP_SATURATION;
   1819        case COMPOSITE_HSL_COLOR:
   1820          return CompositionOp::OP_COLOR;
   1821        case COMPOSITE_HSL_LUMINOSITY:
   1822          return CompositionOp::OP_LUMINOSITY;
   1823      }
   1824    };
   1825    // Short-circuit cases:
   1826    if (compositeMode == COMPOSITE_CLEAR) {
   1827      return true;
   1828    }
   1829    if (compositeMode == COMPOSITE_SRC) {
   1830      return DispatchPaint(aState, aOffset + sourcePaintOffset, aBounds);
   1831    }
   1832    if (compositeMode == COMPOSITE_DEST) {
   1833      return DispatchPaint(aState, aOffset + backdropPaintOffset, aBounds);
   1834    }
   1835 
   1836    // We need bounds for the temporary surfaces; so if we didn't have
   1837    // explicitly-provided bounds from a clipList entry for the top-level
   1838    // glyph, then we need to determine the bounding rect here.
   1839    Rect bounds = aBounds ? *aBounds : GetBoundingRect(aState, aOffset);
   1840    if (bounds.IsEmpty()) {
   1841      return true;
   1842    }
   1843    bounds.RoundOut();
   1844 
   1845    PaintState state = aState;
   1846    state.mDrawOptions.mCompositionOp = CompositionOp::OP_OVER;
   1847    IntSize intSize(int(bounds.width), int(bounds.height));
   1848 
   1849    if (!aState.mDrawTarget->CanCreateSimilarDrawTarget(
   1850            intSize, SurfaceFormat::B8G8R8A8)) {
   1851      // We're not going to be able to render this, so just bail out.
   1852      // (Returning true rather than false means we'll just not paint this
   1853      // part of the glyph, but won't return an error and likely fall back
   1854      // to an ugly black blob.)
   1855      return true;
   1856    }
   1857 
   1858    // Draw the backdrop paint graph to a temporary surface.
   1859    RefPtr backdrop = aState.mDrawTarget->CreateSimilarDrawTarget(
   1860        intSize, SurfaceFormat::B8G8R8A8);
   1861    if (!backdrop) {
   1862      return true;
   1863    }
   1864    backdrop->SetTransform(Matrix::Translation(-bounds.TopLeft()));
   1865    state.mDrawTarget = backdrop;
   1866    if (!DispatchPaint(state, aOffset + backdropPaintOffset, &bounds)) {
   1867      return false;
   1868    }
   1869 
   1870    // Draw the source paint graph to another temp surface.
   1871    RefPtr source = aState.mDrawTarget->CreateSimilarDrawTarget(
   1872        intSize, SurfaceFormat::B8G8R8A8);
   1873    if (!source) {
   1874      return true;
   1875    }
   1876    source->SetTransform(Matrix::Translation(-bounds.TopLeft()));
   1877    state.mDrawTarget = source;
   1878    if (!DispatchPaint(state, aOffset + sourcePaintOffset, &bounds)) {
   1879      return false;
   1880    }
   1881 
   1882    // Composite the source onto the backdrop using the specified operation.
   1883    Rect localBounds(Point(), bounds.Size());
   1884    RefPtr snapshot = source->Snapshot();
   1885    backdrop->SetTransform(Matrix());
   1886    backdrop->DrawSurface(snapshot, localBounds, localBounds,
   1887                          DrawSurfaceOptions(),
   1888                          DrawOptions(1.0, mapCompositionMode(compositeMode)));
   1889 
   1890    // And copy the composited result to our final destination.
   1891    snapshot = backdrop->Snapshot();
   1892    aState.mDrawTarget->DrawSurface(snapshot, bounds, localBounds);
   1893 
   1894    return true;
   1895  }
   1896 
   1897  Rect GetBoundingRect(const PaintState& aState, uint32_t aOffset) const {
   1898    if (!backdropPaintOffset || !sourcePaintOffset) {
   1899      return Rect();
   1900    }
   1901    // For now, just return the maximal bounds that could result; this could be
   1902    // smarter, returning just one of the rects or their intersection when
   1903    // appropriate for the composite mode in effect.
   1904    return DispatchGetBounds(aState, aOffset + backdropPaintOffset)
   1905        .Union(DispatchGetBounds(aState, aOffset + sourcePaintOffset));
   1906  }
   1907 };
   1908 
   1909 #pragma pack()
   1910 
   1911 const BaseGlyphPaintRecord* COLRv1Header::GetBaseGlyphPaint(
   1912    uint32_t aGlyphId) const {
   1913  const auto* list = baseGlyphList();
   1914  if (!list) {
   1915    return nullptr;
   1916  }
   1917  auto compare = [](const void* key, const void* data) -> int {
   1918    uint32_t glyphId = (uint32_t)(uintptr_t)key;
   1919    const auto* paintRecord =
   1920        reinterpret_cast<const BaseGlyphPaintRecord*>(data);
   1921    uint32_t baseGlyphId = uint16_t(paintRecord->glyphID);
   1922    if (baseGlyphId == glyphId) {
   1923      return 0;
   1924    }
   1925    return baseGlyphId > glyphId ? -1 : 1;
   1926  };
   1927  return reinterpret_cast<const BaseGlyphPaintRecord*>(
   1928      bsearch((void*)(uintptr_t)aGlyphId, list + 1,
   1929              uint32_t(list->numBaseGlyphPaintRecords),
   1930              sizeof(BaseGlyphPaintRecord), compare));
   1931 }
   1932 
   1933 #define DO_CASE_VAR(T) \
   1934  DO_CASE(Paint##T);   \
   1935  DO_CASE(PaintVar##T)
   1936 
   1937 // Process paint table at aOffset from start of COLRv1 table.
   1938 static bool DispatchPaint(const PaintState& aState, uint32_t aOffset,
   1939                          const Rect* aBounds) {
   1940  if (aOffset >= aState.mCOLRLength) {
   1941    return false;
   1942  }
   1943 
   1944  const char* paint = aState.COLRv1BaseAddr() + aOffset;
   1945  // All paint table formats start with an 8-bit 'format' field.
   1946  uint8_t format = uint8_t(*paint);
   1947 
   1948 #define DO_CASE(T)                                                         \
   1949  case T::kFormat:                                                         \
   1950    return aOffset + sizeof(T) <= aState.mCOLRLength                       \
   1951               ? reinterpret_cast<const T*>(paint)->Paint(aState, aOffset, \
   1952                                                          aBounds)         \
   1953               : false
   1954 
   1955  switch (format) {
   1956    DO_CASE(PaintColrLayers);
   1957    DO_CASE_VAR(Solid);
   1958    DO_CASE_VAR(LinearGradient);
   1959    DO_CASE_VAR(RadialGradient);
   1960    DO_CASE_VAR(SweepGradient);
   1961    DO_CASE(PaintGlyph);
   1962    DO_CASE(PaintColrGlyph);
   1963    DO_CASE_VAR(Transform);
   1964    DO_CASE_VAR(Translate);
   1965    DO_CASE_VAR(Scale);
   1966    DO_CASE_VAR(ScaleAroundCenter);
   1967    DO_CASE_VAR(ScaleUniform);
   1968    DO_CASE_VAR(ScaleUniformAroundCenter);
   1969    DO_CASE_VAR(Rotate);
   1970    DO_CASE_VAR(RotateAroundCenter);
   1971    DO_CASE_VAR(Skew);
   1972    DO_CASE_VAR(SkewAroundCenter);
   1973    DO_CASE(PaintComposite);
   1974    default:
   1975      break;
   1976  }
   1977 
   1978 #undef DO_CASE
   1979 
   1980  return false;
   1981 }
   1982 
   1983 // Get a gfx::Pattern corresponding to the given paint table, if it is a
   1984 // simple format that can be used as a fill (not a sub-graph).
   1985 static UniquePtr<Pattern> DispatchMakePattern(const PaintState& aState,
   1986                                              uint32_t aOffset) {
   1987  if (aOffset >= aState.mCOLRLength) {
   1988    return nullptr;
   1989  }
   1990 
   1991  const char* paint = aState.COLRv1BaseAddr() + aOffset;
   1992  // All paint table formats start with an 8-bit 'format' field.
   1993  uint8_t format = uint8_t(*paint);
   1994 
   1995 #define DO_CASE(T)                                                       \
   1996  case T::kFormat:                                                       \
   1997    return aOffset + sizeof(T) <= aState.mCOLRLength                     \
   1998               ? reinterpret_cast<const T*>(paint)->MakePattern(aState,  \
   1999                                                                aOffset) \
   2000               : nullptr;
   2001 
   2002  switch (format) {
   2003    DO_CASE_VAR(Solid);
   2004    DO_CASE_VAR(LinearGradient);
   2005    DO_CASE_VAR(RadialGradient);
   2006    DO_CASE_VAR(SweepGradient);
   2007    default:
   2008      break;
   2009  }
   2010 
   2011 #undef DO_CASE
   2012 
   2013  return nullptr;
   2014 }
   2015 
   2016 static Matrix DispatchGetMatrix(const PaintState& aState, uint32_t aOffset) {
   2017  if (aOffset >= aState.mCOLRLength) {
   2018    return Matrix();
   2019  }
   2020 
   2021  const char* paint = aState.COLRv1BaseAddr() + aOffset;
   2022  // All paint table formats start with an 8-bit 'format' field.
   2023  uint8_t format = uint8_t(*paint);
   2024 
   2025 #define DO_CASE(T)                                                             \
   2026  case T::kFormat:                                                             \
   2027    return aOffset + sizeof(T) <= aState.mCOLRLength                           \
   2028               ? reinterpret_cast<const T*>(paint)->GetMatrix(aState, aOffset) \
   2029               : Matrix();
   2030 
   2031  switch (format) {
   2032    DO_CASE_VAR(Transform);
   2033    DO_CASE_VAR(Translate);
   2034    DO_CASE_VAR(Scale);
   2035    DO_CASE_VAR(ScaleAroundCenter);
   2036    DO_CASE_VAR(ScaleUniform);
   2037    DO_CASE_VAR(ScaleUniformAroundCenter);
   2038    DO_CASE_VAR(Rotate);
   2039    DO_CASE_VAR(RotateAroundCenter);
   2040    DO_CASE_VAR(Skew);
   2041    DO_CASE_VAR(SkewAroundCenter);
   2042    default:
   2043      break;
   2044  }
   2045 
   2046 #undef DO_CASE
   2047 
   2048  return Matrix();
   2049 }
   2050 
   2051 static Rect DispatchGetBounds(const PaintState& aState, uint32_t aOffset) {
   2052  if (aOffset >= aState.mCOLRLength) {
   2053    return Rect();
   2054  }
   2055 
   2056  const char* paint = aState.COLRv1BaseAddr() + aOffset;
   2057  // All paint table formats start with an 8-bit 'format' field.
   2058  uint8_t format = uint8_t(*paint);
   2059 
   2060 #define DO_CASE(T)                                                           \
   2061  case T::kFormat:                                                           \
   2062    return aOffset + sizeof(T) <= aState.mCOLRLength                         \
   2063               ? reinterpret_cast<const T*>(paint)->GetBoundingRect(aState,  \
   2064                                                                    aOffset) \
   2065               : Rect();
   2066 
   2067  switch (format) {
   2068    DO_CASE(PaintColrLayers);
   2069    DO_CASE_VAR(Solid);
   2070    DO_CASE_VAR(LinearGradient);
   2071    DO_CASE_VAR(RadialGradient);
   2072    DO_CASE_VAR(SweepGradient);
   2073    DO_CASE(PaintGlyph);
   2074    DO_CASE(PaintColrGlyph);
   2075    DO_CASE_VAR(Transform);
   2076    DO_CASE_VAR(Translate);
   2077    DO_CASE_VAR(Scale);
   2078    DO_CASE_VAR(ScaleAroundCenter);
   2079    DO_CASE_VAR(ScaleUniform);
   2080    DO_CASE_VAR(ScaleUniformAroundCenter);
   2081    DO_CASE_VAR(Rotate);
   2082    DO_CASE_VAR(RotateAroundCenter);
   2083    DO_CASE_VAR(Skew);
   2084    DO_CASE_VAR(SkewAroundCenter);
   2085    DO_CASE(PaintComposite);
   2086    default:
   2087      break;
   2088  }
   2089 
   2090 #undef DO_CASE
   2091 
   2092  return Rect();
   2093 }
   2094 
   2095 #undef DO_CASE_VAR
   2096 
   2097 bool COLRHeader::Validate(uint64_t aLength) const {
   2098  uint64_t count;
   2099  if ((count = numBaseGlyphRecords)) {
   2100    if (baseGlyphRecordsOffset + count * sizeof(BaseGlyphRecord) > aLength) {
   2101      return false;
   2102    }
   2103  }
   2104  if ((count = numLayerRecords)) {
   2105    if (layerRecordsOffset + count * sizeof(LayerRecord) > aLength) {
   2106      return false;
   2107    }
   2108  }
   2109  // Check ordering of baseGlyphRecords, and that layer indices are in bounds.
   2110  int32_t lastGlyphId = -1;
   2111  const auto* baseGlyph = reinterpret_cast<const BaseGlyphRecord*>(
   2112      reinterpret_cast<const char*>(this) + baseGlyphRecordsOffset);
   2113  for (uint16_t i = 0; i < uint16_t(numBaseGlyphRecords); i++, baseGlyph++) {
   2114    uint16_t glyphId = baseGlyph->glyphId;
   2115    if (lastGlyphId >= int32_t(glyphId)) {
   2116      return false;
   2117    }
   2118    if (uint32_t(baseGlyph->firstLayerIndex) + uint32_t(baseGlyph->numLayers) >
   2119        uint32_t(numLayerRecords)) {
   2120      return false;
   2121    }
   2122    lastGlyphId = glyphId;
   2123  }
   2124  // We don't need to validate all the layer paletteEntryIndex fields here,
   2125  // because PaintState.GetColor will range-check them at paint time.
   2126  return true;
   2127 }
   2128 
   2129 bool COLRv1Header::Validate(uint64_t aLength) const {
   2130  if (!base.Validate(aLength)) {
   2131    return false;
   2132  }
   2133  if (baseGlyphListOffset + sizeof(BaseGlyphList) > aLength ||
   2134      layerListOffset + sizeof(LayerList) > aLength ||
   2135      clipListOffset + sizeof(ClipList) > aLength ||
   2136      varIndexMapOffset + sizeof(DeltaSetIndexMap) > aLength ||
   2137      itemVariationStoreOffset + sizeof(ItemVariationStore) > aLength) {
   2138    return false;
   2139  }
   2140  const auto* b = baseGlyphList();
   2141  if (b && !b->Validate(this, aLength)) {
   2142    return false;
   2143  }
   2144  const auto* l = layerList();
   2145  if (l && !l->Validate(this, aLength)) {
   2146    return false;
   2147  }
   2148  const auto* c = clipList();
   2149  if (c && !c->Validate(this, aLength)) {
   2150    return false;
   2151  }
   2152  const auto* v = varIndexMap();
   2153  if (v && !v->Validate(this, aLength)) {
   2154    return false;
   2155  }
   2156  const auto* i = itemVariationStore();
   2157  if (i && !i->Validate(this, aLength)) {
   2158    return false;
   2159  }
   2160  return true;
   2161 }
   2162 
   2163 bool BaseGlyphList::Validate(const COLRv1Header* aHeader,
   2164                             uint64_t aLength) const {
   2165  uint64_t count = numBaseGlyphPaintRecords;
   2166  if (aHeader->baseGlyphListOffset + sizeof(BaseGlyphList) +
   2167          count * sizeof(BaseGlyphPaintRecord) >
   2168      aLength) {
   2169    return false;
   2170  }
   2171  // Check ordering of baseGlyphPaint records.
   2172  const auto* records = baseGlyphPaintRecords();
   2173  int32_t prevGlyphID = -1;
   2174  for (uint32_t i = 0; i < numBaseGlyphPaintRecords; ++i) {
   2175    const auto& base = records[i];
   2176    if (int32_t(uint16_t(base.glyphID)) <= prevGlyphID) {
   2177      return false;
   2178    }
   2179    prevGlyphID = base.glyphID;
   2180  }
   2181  return true;
   2182 }
   2183 
   2184 bool LayerList::Validate(const COLRv1Header* aHeader, uint64_t aLength) const {
   2185  // Check that paintOffsets array fits.
   2186  uint64_t count = numLayers;
   2187  uint32_t listOffset = aHeader->layerListOffset;
   2188  if (listOffset + sizeof(LayerList) + count * sizeof(uint32) > aLength) {
   2189    return false;
   2190  }
   2191  // Check that values in paintOffsets are within bounds.
   2192  const auto* offsets = paintOffsets();
   2193  for (uint32_t i = 0; i < count; i++) {
   2194    if (listOffset + offsets[i] >= aLength) {
   2195      return false;
   2196    }
   2197  }
   2198  return true;
   2199 }
   2200 
   2201 bool Clip::Validate(const COLRv1Header* aHeader, uint64_t aLength) const {
   2202  uint32_t offset = aHeader->clipListOffset + clipBoxOffset;
   2203  if (offset >= aLength) {
   2204    return false;
   2205  }
   2206  // ClipBox format begins with a 1-byte format field.
   2207  const uint8_t* box = reinterpret_cast<const uint8_t*>(aHeader) + offset;
   2208  switch (*box) {
   2209    case 1:
   2210      if (offset <= aLength - sizeof(ClipBoxFormat1)) {
   2211        return true;
   2212      }
   2213      break;
   2214    case 2:
   2215      if (offset <= aLength - sizeof(ClipBoxFormat2)) {
   2216        return true;
   2217      }
   2218      break;
   2219    default:
   2220      // unknown ClipBoxFormat
   2221      break;
   2222  }
   2223  return false;
   2224 }
   2225 
   2226 bool ClipList::Validate(const COLRv1Header* aHeader, uint64_t aLength) const {
   2227  uint64_t count = numClips;
   2228  if (aHeader->clipListOffset + sizeof(ClipList) + count * sizeof(Clip) >
   2229      aLength) {
   2230    return false;
   2231  }
   2232  // Check ordering of clip records, and that they are within bounds.
   2233  const auto* clipArray = clips();
   2234  int32_t prevEnd = -1;
   2235  for (uint32_t i = 0; i < count; ++i) {
   2236    const auto& clip = clipArray[i];
   2237    if (int32_t(uint16_t(clip.startGlyphID)) <= prevEnd) {
   2238      return false;
   2239    }
   2240    if (!clip.Validate(aHeader, aLength)) {
   2241      return false;
   2242    }
   2243    prevEnd = uint16_t(clip.endGlyphID);
   2244  }
   2245  return true;
   2246 }
   2247 
   2248 bool DeltaSetIndexMap::Validate(const COLRv1Header* aHeader,
   2249                                uint64_t aLength) const {
   2250  uint64_t entrySize = ((entryFormat & MAP_ENTRY_SIZE_MASK) >> 4) + 1;
   2251  uint64_t mapCount;
   2252  uint64_t baseSize;
   2253  switch (format) {
   2254    case 0:
   2255      mapCount = uint32_t(v0.mapCount);
   2256      baseSize = 4;
   2257      break;
   2258    case 1:
   2259      mapCount = uint32_t(v1.mapCount);
   2260      baseSize = 6;
   2261      break;
   2262    default:
   2263      return false;
   2264  }
   2265  if (aHeader->varIndexMapOffset + baseSize + mapCount * entrySize > aLength) {
   2266    return false;
   2267  }
   2268  return true;
   2269 }
   2270 
   2271 bool ItemVariationStore::Validate(const COLRv1Header* aHeader,
   2272                                  uint64_t aLength) const {
   2273  uint64_t offset = reinterpret_cast<const char*>(this) -
   2274                    reinterpret_cast<const char*>(aHeader);
   2275  if (offset + variationRegionListOffset + sizeof(VariationRegionList) >
   2276      aLength) {
   2277    return false;
   2278  }
   2279  if (!variationRegionList()->Validate(aHeader, aLength)) {
   2280    return false;
   2281  }
   2282  uint16_t count = itemVariationDataCount;
   2283  if (offset + sizeof(ItemVariationStore) + count * sizeof(Offset32) >
   2284      aLength) {
   2285    return false;
   2286  }
   2287  const auto* ivdOffsets = itemVariationDataOffsets();
   2288  for (uint16_t i = 0; i < count; ++i) {
   2289    uint32_t o = ivdOffsets[i];
   2290    if (offset + o + sizeof(ItemVariationData) > aLength) {
   2291      return false;
   2292    }
   2293    const auto* variationData = reinterpret_cast<const ItemVariationData*>(
   2294        reinterpret_cast<const char*>(this) + ivdOffsets[i]);
   2295    if (!variationData->Validate(aHeader, aLength)) {
   2296      return false;
   2297    }
   2298  }
   2299  return true;
   2300 }
   2301 
   2302 bool ItemVariationData::Validate(const COLRv1Header* aHeader,
   2303                                 uint64_t aLength) const {
   2304  if (reinterpret_cast<const char*>(regionIndexes() +
   2305                                    uint16_t(regionIndexCount)) >
   2306      reinterpret_cast<const char*>(aHeader) + aLength) {
   2307    return false;
   2308  }
   2309  uint16_t wordDeltaCount = this->wordDeltaCount;
   2310  bool longWords = wordDeltaCount & LONG_WORDS;
   2311  wordDeltaCount &= WORD_DELTA_COUNT_MASK;
   2312  uint32_t deltaSetSize =
   2313      (uint16_t(regionIndexCount) + uint16_t(wordDeltaCount)) << longWords;
   2314  if (reinterpret_cast<const char*>(deltaSets()) +
   2315          uint16_t(itemCount) * deltaSetSize >
   2316      reinterpret_cast<const char*>(aHeader) + aLength) {
   2317    return false;
   2318  }
   2319  return true;
   2320 }
   2321 
   2322 }  // end anonymous namespace
   2323 
   2324 namespace mozilla::gfx {
   2325 
   2326 bool COLRFonts::ValidateColorGlyphs(hb_blob_t* aCOLR, hb_blob_t* aCPAL) {
   2327  struct ColorRecord {
   2328    uint8_t blue;
   2329    uint8_t green;
   2330    uint8_t red;
   2331    uint8_t alpha;
   2332  };
   2333 
   2334  struct CPALHeaderVersion0 {
   2335    uint16 version;
   2336    uint16 numPaletteEntries;
   2337    uint16 numPalettes;
   2338    uint16 numColorRecords;
   2339    Offset32 colorRecordsArrayOffset;
   2340    // uint16 	colorRecordIndices[numPalettes];
   2341    const uint16* colorRecordIndices() const {
   2342      return reinterpret_cast<const uint16*>(this + 1);
   2343    }
   2344  };
   2345 
   2346  unsigned int cpalLength;
   2347  const CPALHeaderVersion0* cpal = reinterpret_cast<const CPALHeaderVersion0*>(
   2348      hb_blob_get_data(aCPAL, &cpalLength));
   2349  if (!cpal || cpalLength < sizeof(CPALHeaderVersion0)) {
   2350    return false;
   2351  }
   2352 
   2353  // We can handle either version 0 or 1.
   2354  if (uint16_t(cpal->version) > 1) {
   2355    return false;
   2356  }
   2357 
   2358  uint16_t numPaletteEntries = cpal->numPaletteEntries;
   2359  uint16_t numPalettes = cpal->numPalettes;
   2360  uint16_t numColorRecords = cpal->numColorRecords;
   2361  uint32_t colorRecordsArrayOffset = cpal->colorRecordsArrayOffset;
   2362  const auto* indices = cpal->colorRecordIndices();
   2363  if (colorRecordsArrayOffset >= cpalLength) {
   2364    return false;
   2365  }
   2366  if (!numPaletteEntries || !numPalettes ||
   2367      numColorRecords < numPaletteEntries) {
   2368    return false;
   2369  }
   2370  if (sizeof(ColorRecord) * numColorRecords >
   2371      cpalLength - colorRecordsArrayOffset) {
   2372    return false;
   2373  }
   2374  if (sizeof(uint16) * numPalettes > cpalLength - sizeof(CPALHeaderVersion0)) {
   2375    return false;
   2376  }
   2377  for (uint16_t i = 0; i < numPalettes; ++i) {
   2378    uint32_t index = indices[i];
   2379    if (index + numPaletteEntries > numColorRecords) {
   2380      return false;
   2381    }
   2382  }
   2383 
   2384  // The additional fields in CPALv1 are not checked here; the harfbuzz code
   2385  // handles reading them safely.
   2386 
   2387  unsigned int colrLength;
   2388  const COLRHeader* colr =
   2389      reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR, &colrLength));
   2390  if (!colr || colrLength < sizeof(COLRHeader)) {
   2391    return false;
   2392  }
   2393 
   2394  if (uint16_t(colr->version) == 1) {
   2395    return colrLength >= sizeof(COLRv1Header) &&
   2396           reinterpret_cast<const COLRv1Header*>(colr)->Validate(colrLength);
   2397  }
   2398 
   2399  if (uint16_t(colr->version) != 0) {
   2400    // We only support version 1 (above) or version 0 headers.
   2401    return false;
   2402  }
   2403 
   2404  return colr->Validate(colrLength);
   2405 }
   2406 
   2407 const COLRFonts::GlyphLayers* COLRFonts::GetGlyphLayers(hb_blob_t* aCOLR,
   2408                                                        uint32_t aGlyphId) {
   2409  unsigned int length;
   2410  const COLRHeader* colr =
   2411      reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR, &length));
   2412  // This should never be called unless we have checked that the COLR table is
   2413  // structurally valid, so it will be safe to read the header fields.
   2414  MOZ_RELEASE_ASSERT(colr && length >= sizeof(COLRHeader), "bad COLR table!");
   2415  auto compareBaseGlyph = [](const void* key, const void* data) -> int {
   2416    uint32_t glyphId = (uint32_t)(uintptr_t)key;
   2417    const auto* baseGlyph = reinterpret_cast<const BaseGlyphRecord*>(data);
   2418    uint32_t baseGlyphId = uint16_t(baseGlyph->glyphId);
   2419    if (baseGlyphId == glyphId) {
   2420      return 0;
   2421    }
   2422    return baseGlyphId > glyphId ? -1 : 1;
   2423  };
   2424  return reinterpret_cast<const GlyphLayers*>(
   2425      bsearch((void*)(uintptr_t)aGlyphId, colr->GetBaseGlyphRecords(),
   2426              uint16_t(colr->numBaseGlyphRecords), sizeof(BaseGlyphRecord),
   2427              compareBaseGlyph));
   2428 }
   2429 
   2430 bool COLRFonts::PaintGlyphLayers(
   2431    hb_blob_t* aCOLR, hb_face_t* aFace, const GlyphLayers* aLayers,
   2432    DrawTarget* aDrawTarget, layout::TextDrawTarget* aTextDrawer,
   2433    ScaledFont* aScaledFont, DrawOptions aDrawOptions, const Point& aPoint,
   2434    const sRGBColor& aCurrentColor, const nsTArray<sRGBColor>* aColors) {
   2435  const auto* glyphRecord = reinterpret_cast<const BaseGlyphRecord*>(aLayers);
   2436  // Default to opaque rendering (non-webrender applies alpha with a layer)
   2437  float alpha = 1.0;
   2438  if (aTextDrawer) {
   2439    // defaultColor is the one that comes from CSS, so it has transparency info.
   2440    bool hasComplexTransparency =
   2441        0.0 < aCurrentColor.a && aCurrentColor.a < 1.0;
   2442    if (hasComplexTransparency && uint16_t(glyphRecord->numLayers) > 1) {
   2443      // WebRender doesn't support drawing multi-layer transparent color-glyphs,
   2444      // as it requires compositing all the layers before applying transparency.
   2445      // (pretend to succeed, output doesn't matter, we will emit a blob)
   2446      aTextDrawer->FoundUnsupportedFeature();
   2447      return true;
   2448    }
   2449 
   2450    // If we get here, then either alpha is 0 or 1, or there's only one layer
   2451    // which shouldn't have composition issues. In all of these cases, applying
   2452    // transparency directly to the glyph should work perfectly fine.
   2453    //
   2454    // Note that we must still emit completely transparent emoji, because they
   2455    // might be wrapped in a shadow that uses the text run's glyphs.
   2456    alpha = aCurrentColor.a;
   2457  }
   2458 
   2459  unsigned int length;
   2460  const COLRHeader* colr =
   2461      reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR, &length));
   2462  PaintState state{{colr},
   2463                   aColors->Elements(),
   2464                   aDrawTarget,
   2465                   aScaledFont,
   2466                   nullptr,  // variations not needed
   2467                   aDrawOptions,
   2468                   length,
   2469                   aCurrentColor,
   2470                   0.0,  // fontUnitsToPixels not needed
   2471                   uint16_t(aColors->Length()),
   2472                   0,
   2473                   nullptr};
   2474  return glyphRecord->Paint(state, alpha, aPoint);
   2475 }
   2476 
   2477 const COLRFonts::GlyphPaintGraph* COLRFonts::GetGlyphPaintGraph(
   2478    hb_blob_t* aCOLR, uint32_t aGlyphId) {
   2479  if (!StaticPrefs::gfx_font_rendering_colr_v1_enabled()) {
   2480    return nullptr;
   2481  }
   2482  unsigned int blobLength;
   2483  const auto* colr =
   2484      reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR, &blobLength));
   2485  MOZ_ASSERT(colr, "Cannot get COLR raw data");
   2486  MOZ_ASSERT(blobLength >= sizeof(COLRHeader), "COLR data too small");
   2487 
   2488  uint16_t version = colr->version;
   2489  if (version == 1) {
   2490    MOZ_ASSERT(blobLength >= sizeof(COLRv1Header), "COLRv1 data too small");
   2491    const auto* colrv1 = reinterpret_cast<const COLRv1Header*>(colr);
   2492    return reinterpret_cast<const GlyphPaintGraph*>(
   2493        colrv1->GetBaseGlyphPaint(aGlyphId));
   2494  }
   2495 
   2496  return nullptr;
   2497 }
   2498 
   2499 bool COLRFonts::PaintGlyphGraph(
   2500    hb_blob_t* aCOLR, hb_font_t* aFont, const GlyphPaintGraph* aPaintGraph,
   2501    DrawTarget* aDrawTarget, layout::TextDrawTarget* aTextDrawer,
   2502    ScaledFont* aScaledFont, DrawOptions aDrawOptions, const Point& aPoint,
   2503    const sRGBColor& aCurrentColor, const nsTArray<sRGBColor>* aColors,
   2504    uint32_t aGlyphId, float aFontUnitsToPixels) {
   2505  if (aTextDrawer) {
   2506    // Currently we always punt to a blob for COLRv1 glyphs.
   2507    aTextDrawer->FoundUnsupportedFeature();
   2508    return true;
   2509  }
   2510 
   2511  unsigned int coordCount;
   2512  const int* coords = hb_font_get_var_coords_normalized(aFont, &coordCount);
   2513 
   2514  AutoTArray<uint32_t, 32> visitedOffsets;
   2515  PaintState state{{nullptr},
   2516                   aColors->Elements(),
   2517                   aDrawTarget,
   2518                   aScaledFont,
   2519                   coords,
   2520                   aDrawOptions,
   2521                   hb_blob_get_length(aCOLR),
   2522                   aCurrentColor,
   2523                   aFontUnitsToPixels,
   2524                   uint16_t(aColors->Length()),
   2525                   uint16_t(coordCount),
   2526                   &visitedOffsets};
   2527  state.mHeader.v1 =
   2528      reinterpret_cast<const COLRv1Header*>(hb_blob_get_data(aCOLR, nullptr));
   2529  AutoRestoreTransform saveTransform(aDrawTarget);
   2530  aDrawTarget->ConcatTransform(Matrix::Translation(aPoint));
   2531  return PaintColrGlyph::DoPaint(
   2532      state, reinterpret_cast<const BaseGlyphPaintRecord*>(aPaintGraph),
   2533      aGlyphId, nullptr);
   2534 }
   2535 
   2536 Rect COLRFonts::GetColorGlyphBounds(hb_blob_t* aCOLR, hb_font_t* aFont,
   2537                                    uint32_t aGlyphId, DrawTarget* aDrawTarget,
   2538                                    ScaledFont* aScaledFont,
   2539                                    float aFontUnitsToPixels) {
   2540  unsigned int coordCount;
   2541  const int* coords = hb_font_get_var_coords_normalized(aFont, &coordCount);
   2542 
   2543  AutoTArray<uint32_t, 32> visitedOffsets;
   2544  PaintState state{{nullptr},
   2545                   nullptr,  // palette is not needed
   2546                   aDrawTarget,
   2547                   aScaledFont,
   2548                   coords,
   2549                   DrawOptions(),
   2550                   hb_blob_get_length(aCOLR),
   2551                   sRGBColor(),
   2552                   aFontUnitsToPixels,
   2553                   0,  // numPaletteEntries
   2554                   uint16_t(coordCount),
   2555                   &visitedOffsets};
   2556  state.mHeader.v1 =
   2557      reinterpret_cast<const COLRv1Header*>(hb_blob_get_data(aCOLR, nullptr));
   2558  MOZ_ASSERT(uint16_t(state.mHeader.v1->base.version) == 1);
   2559  // If a clip rect is provided, return this as the glyph bounds.
   2560  const auto* clipList = state.mHeader.v1->clipList();
   2561  if (clipList) {
   2562    const auto* clip = clipList->GetClip(aGlyphId);
   2563    if (clip) {
   2564      return clip->GetRect(state);
   2565    }
   2566  }
   2567  // Otherwise, compute bounds by walking the paint graph.
   2568  const auto* base = state.mHeader.v1->GetBaseGlyphPaint(aGlyphId);
   2569  if (base) {
   2570    return DispatchGetBounds(
   2571        state, state.mHeader.v1->baseGlyphListOffset + base->paintOffset);
   2572  }
   2573  return Rect();
   2574 }
   2575 
   2576 uint16_t COLRFonts::GetColrTableVersion(hb_blob_t* aCOLR) {
   2577  unsigned int blobLength;
   2578  const auto* colr =
   2579      reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR, &blobLength));
   2580  MOZ_ASSERT(colr, "Cannot get COLR raw data");
   2581  MOZ_ASSERT(blobLength >= sizeof(COLRHeader), "COLR data too small");
   2582  return colr->version;
   2583 }
   2584 
   2585 nsTArray<sRGBColor> COLRFonts::CreateColorPalette(
   2586    hb_face_t* aFace, const FontPaletteValueSet* aPaletteValueSet,
   2587    nsAtom* aFontPalette, const nsACString& aFamilyName) {
   2588  // Find the base color palette to use, if there are multiple available;
   2589  // default to first in the font, if nothing matches what is requested.
   2590  unsigned int paletteIndex = 0;
   2591  unsigned int count = hb_ot_color_palette_get_count(aFace);
   2592  MOZ_ASSERT(count > 0, "No palettes? Font should have been rejected!");
   2593 
   2594  const FontPaletteValueSet::PaletteValues* fpv = nullptr;
   2595  if (aFontPalette && aFontPalette != nsGkAtoms::normal &&
   2596      (count > 1 || aPaletteValueSet)) {
   2597    auto findPalette = [&](hb_ot_color_palette_flags_t flag) -> unsigned int {
   2598      MOZ_ASSERT(flag != HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
   2599      for (unsigned int i = 0; i < count; ++i) {
   2600        if (hb_ot_color_palette_get_flags(aFace, i) & flag) {
   2601          return i;
   2602        }
   2603      }
   2604      return 0;
   2605    };
   2606 
   2607    if (aFontPalette == nsGkAtoms::light) {
   2608      paletteIndex =
   2609          findPalette(HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND);
   2610    } else if (aFontPalette == nsGkAtoms::dark) {
   2611      paletteIndex =
   2612          findPalette(HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND);
   2613    } else {
   2614      if (aPaletteValueSet) {
   2615        if ((fpv = aPaletteValueSet->Lookup(aFontPalette, aFamilyName))) {
   2616          if (fpv->mBasePalette >= 0 && fpv->mBasePalette < int32_t(count)) {
   2617            paletteIndex = fpv->mBasePalette;
   2618          } else if (fpv->mBasePalette ==
   2619                     FontPaletteValueSet::PaletteValues::kLight) {
   2620            paletteIndex = findPalette(
   2621                HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND);
   2622          } else if (fpv->mBasePalette ==
   2623                     FontPaletteValueSet::PaletteValues::kDark) {
   2624            paletteIndex = findPalette(
   2625                HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND);
   2626          }
   2627        }
   2628      }
   2629    }
   2630  }
   2631 
   2632  // Collect the palette colors and convert them to sRGBColor values.
   2633  count =
   2634      hb_ot_color_palette_get_colors(aFace, paletteIndex, 0, nullptr, nullptr);
   2635  nsTArray<hb_color_t> colors;
   2636  colors.SetLength(count);
   2637  hb_ot_color_palette_get_colors(aFace, paletteIndex, 0, &count,
   2638                                 colors.Elements());
   2639 
   2640  nsTArray<sRGBColor> palette;
   2641  palette.SetCapacity(count);
   2642  for (const auto c : colors) {
   2643    palette.AppendElement(
   2644        sRGBColor(hb_color_get_red(c) / 255.0, hb_color_get_green(c) / 255.0,
   2645                  hb_color_get_blue(c) / 255.0, hb_color_get_alpha(c) / 255.0));
   2646  }
   2647 
   2648  // Apply @font-palette-values overrides, if present.
   2649  if (fpv) {
   2650    for (const auto overrideColor : fpv->mOverrides) {
   2651      if (overrideColor.mIndex < palette.Length()) {
   2652        palette[overrideColor.mIndex] = overrideColor.mColor;
   2653      }
   2654    }
   2655  }
   2656 
   2657  return palette;
   2658 }
   2659 
   2660 const FontPaletteValueSet::PaletteValues* FontPaletteValueSet::Lookup(
   2661    nsAtom* aName, const nsACString& aFamily) const {
   2662  nsAutoCString family(aFamily);
   2663  ToLowerCase(family);
   2664  if (const HashEntry* entry =
   2665          mValues.GetEntry(PaletteHashKey(aName, family))) {
   2666    return &entry->mValue;
   2667  }
   2668  return nullptr;
   2669 }
   2670 
   2671 FontPaletteValueSet::PaletteValues* FontPaletteValueSet::Insert(
   2672    nsAtom* aName, const nsACString& aFamily) {
   2673  nsAutoCString family(aFamily);
   2674  ToLowerCase(family);
   2675  HashEntry* entry = mValues.PutEntry(PaletteHashKey(aName, family));
   2676  return &entry->mValue;
   2677 }
   2678 
   2679 }  // end namespace mozilla::gfx