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/. */
7 #include "gfxFontUtils.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"
18 using namespace mozilla
;
19 using namespace mozilla::gfx
;
21 namespace { // anonymous namespace for implementation internals
23 #pragma pack(1) // ensure no padding is added to the COLR structs
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
;
39 struct BaseGlyphRecord
;
40 struct DeltaSetIndexMap
;
41 struct ItemVariationStore
;
45 uint16 numBaseGlyphRecords
;
46 Offset32 baseGlyphRecordsOffset
;
47 Offset32 layerRecordsOffset
;
48 uint16 numLayerRecords
;
50 const BaseGlyphRecord
* GetBaseGlyphRecords() const {
51 return reinterpret_cast<const BaseGlyphRecord
*>(
52 reinterpret_cast<const char*>(this) + baseGlyphRecordsOffset
);
55 const LayerRecord
* GetLayerRecords() const {
56 return reinterpret_cast<const LayerRecord
*>(
57 reinterpret_cast<const char*>(this) + layerRecordsOffset
);
60 bool Validate(uint64_t aLength
) const;
63 struct BaseGlyphPaintRecord
{
68 struct BaseGlyphList
{
69 uint32 numBaseGlyphPaintRecords
;
70 // BaseGlyphPaintRecord baseGlyphPaintRecords[numBaseGlyphPaintRecords];
71 const BaseGlyphPaintRecord
* baseGlyphPaintRecords() const {
72 return reinterpret_cast<const BaseGlyphPaintRecord
*>(this + 1);
74 bool Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const;
79 // uint32 paintOffsets[numLayers];
80 const uint32
* paintOffsets() const {
81 return reinterpret_cast<const uint32
*>(this + 1);
83 bool Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const;
88 Offset32 baseGlyphListOffset
;
89 Offset32 layerListOffset
;
90 Offset32 clipListOffset
;
91 Offset32 varIndexMapOffset
;
92 Offset32 itemVariationStoreOffset
;
94 bool Validate(uint64_t aLength
) const;
96 const BaseGlyphList
* baseGlyphList() const {
97 uint32_t offset
= baseGlyphListOffset
;
101 const char* ptr
= reinterpret_cast<const char*>(this) + offset
;
102 return reinterpret_cast<const struct BaseGlyphList
*>(ptr
);
105 const LayerList
* layerList() const {
106 uint32_t offset
= layerListOffset
;
110 const char* ptr
= reinterpret_cast<const char*>(this) + offset
;
111 return reinterpret_cast<const LayerList
*>(ptr
);
114 const struct ClipList
* clipList() const {
115 uint32_t offset
= clipListOffset
;
119 const char* ptr
= reinterpret_cast<const char*>(this) + offset
;
120 return reinterpret_cast<const ClipList
*>(ptr
);
123 const struct DeltaSetIndexMap
* varIndexMap() const {
124 uint32_t offset
= varIndexMapOffset
;
128 const char* ptr
= reinterpret_cast<const char*>(this) + offset
;
129 return reinterpret_cast<const DeltaSetIndexMap
*>(ptr
);
132 const struct ItemVariationStore
* itemVariationStore() const {
133 uint32_t offset
= itemVariationStoreOffset
;
137 const char* ptr
= reinterpret_cast<const char*>(this) + offset
;
138 return reinterpret_cast<const ItemVariationStore
*>(ptr
);
141 const BaseGlyphPaintRecord
* GetBaseGlyphPaint(uint32_t aGlyphId
) const;
146 const COLRHeader
* v0
;
147 const COLRv1Header
* v1
;
149 const sRGBColor
* mPalette
;
150 DrawTarget
* mDrawTarget
;
151 ScaledFont
* mScaledFont
;
153 DrawOptions mDrawOptions
;
154 uint32_t mCOLRLength
;
155 sRGBColor mCurrentColor
;
156 float mFontUnitsToPixels
;
158 uint16_t mCoordCount
;
159 nsTArray
<uint32_t>* mVisited
;
161 const char* COLRv1BaseAddr() const {
162 return reinterpret_cast<const char*>(mHeader
.v1
);
165 DeviceColor
GetColor(uint16_t aPaletteIndex
, float aAlpha
) const;
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
);
174 DeviceColor
PaintState::GetColor(uint16_t aPaletteIndex
, float aAlpha
) const {
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.
184 return ToDeviceColor(color
);
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
,
191 static Rect
DispatchGetBounds(const PaintState
& aState
, uint32_t aOffset
);
192 static Matrix
DispatchGetMatrix(const PaintState
& aState
, uint32_t aOffset
);
194 // Variation-data types
196 enum { kFractionBits
= 16 };
197 operator float() const {
198 return float(int32_t(value
)) / float(1 << kFractionBits
);
200 int32_t intRepr() const { return int32_t(value
); }
207 enum { kFractionBits
= 14 };
208 operator float() const {
209 return float(int16_t(value
)) / float(1 << kFractionBits
);
211 int32_t intRepr() const { return int16_t(value
); }
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
) {
222 return std::numeric_limits
<uint32_t>::max();
225 struct RegionAxisCoordinates
{
231 struct VariationRegion
{
232 // RegionAxisCoordinates regionAxes[axisCount];
233 const RegionAxisCoordinates
* regionAxes() const {
234 return reinterpret_cast<const RegionAxisCoordinates
*>(this);
238 struct VariationRegionList
{
241 // VariationRegion variationRegions[regionCount];
242 const char* variationRegionsBase() const {
243 return reinterpret_cast<const char*>(this + 1);
245 size_t regionSize() const {
246 return uint16_t(axisCount
) * sizeof(RegionAxisCoordinates
);
248 const VariationRegion
* getRegion(uint16_t i
) const {
249 if (i
>= uint16_t(regionCount
)) {
252 return reinterpret_cast<const VariationRegion
*>(variationRegionsBase() +
255 bool Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const {
256 if (variationRegionsBase() - reinterpret_cast<const char*>(aHeader
) +
257 uint16_t(regionCount
) * regionSize() >
268 // (int32 and int16) deltaData[regionIndexCount];
271 struct DeltaSetIndexMap
{
272 enum { INNER_INDEX_BIT_COUNT_MASK
= 0x0f, MAP_ENTRY_SIZE_MASK
= 0x30 };
278 // uint8 mapData[variable];
282 // uint8 mapData[variable];
285 uint32_t entrySize() const {
286 return (((entryFormat
& MAP_ENTRY_SIZE_MASK
) >> 4) + 1);
288 uint32_t map(uint32_t aIndex
) const {
290 const uint8_t* mapData
;
293 mapCount
= uint32_t(v0
.mapCount
);
294 mapData
= reinterpret_cast<const uint8_t*>(&v0
.mapCount
) +
298 mapCount
= uint32_t(v1
.mapCount
);
299 mapData
= reinterpret_cast<const uint8_t*>(&v1
.mapCount
) +
303 // unknown DeltaSetIndexMap format
309 if (aIndex
>= mapCount
) {
310 aIndex
= mapCount
- 1;
312 const uint8_t* entryData
= mapData
+ aIndex
* entrySize();
314 for (uint32_t i
= 0; i
< entrySize(); ++i
) {
315 entry
= (entry
<< 8) + entryData
[i
];
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
;
323 bool Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const;
326 enum EntryFormatMasks
{
327 INNER_INDEX_BIT_COUNT_MASK
= 0x0f,
328 MAP_ENTRY_SIZE_MASK
= 0x30
331 struct ItemVariationData
{
332 enum { WORD_DELTA_COUNT_MASK
= 0x7FFF, LONG_WORDS
= 0x8000 };
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));
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
));
347 bool Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const;
350 struct ItemVariationStore
{
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));
359 const VariationRegionList
* variationRegionList() const {
360 return reinterpret_cast<const VariationRegionList
*>(
361 reinterpret_cast<const char*>(this) + variationRegionListOffset
);
363 bool Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const;
366 static int32_t ApplyVariation(const PaintState
& aState
, int32_t aValue
,
368 if (aIndex
== 0xffffffff) {
371 const auto* store
= aState
.mHeader
.v1
->itemVariationStore();
372 if (!store
|| uint16_t(store
->format
) != 1) {
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
]) {
385 const auto* regionList
= store
->variationRegionList();
386 if (outerIndex
>= uint16_t(store
->itemVariationDataCount
)) {
389 const auto* variationData
= reinterpret_cast<const ItemVariationData
*>(
390 reinterpret_cast<const char*>(store
) +
391 itemVariationDataOffsets
[outerIndex
]);
392 if (innerIndex
>= uint16_t(variationData
->itemCount
)) {
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
) {
410 const auto* region
= regionList
->getRegion(uint16_t(regionIndexes
[i
]));
414 // XXX Should we do the calculations here in fixed-point? Check spec.
416 for (uint16_t axisIndex
= 0; axisIndex
< uint16_t(regionList
->axisCount
);
418 const auto& axis
= region
->regionAxes()[axisIndex
];
419 float peak
= axis
.peakCoord
;
421 // This axis cannot contribute to scalar.
424 float start
= axis
.startCoord
;
425 float end
= axis
.endCoord
;
426 float value
= axisIndex
< aState
.mCoordCount
427 ? float(aState
.mCoords
[axisIndex
]) / 16384.0f
429 if (value
< start
|| value
> end
) {
430 // Out of range: this region is not applicable.
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
);
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
];
453 delta
= int32_t(floorf((float(delta
) * scalar
) + 0.5f
));
459 static float ApplyVariation(const PaintState
& aState
, Fixed aValue
,
461 return float(ApplyVariation(aState
, aValue
.intRepr(), aIndex
)) /
462 (1 << Fixed::kFractionBits
);
465 static float ApplyVariation(const PaintState
& aState
, F2DOT14 aValue
,
467 return float(ApplyVariation(aState
, aValue
.intRepr(), aIndex
)) /
468 (1 << F2DOT14::kFractionBits
);
471 struct ClipBoxFormat1
{
472 enum { kFormat
= 1 };
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
));
491 struct ClipBoxFormat2
: public ClipBoxFormat1
{
492 enum { kFormat
= 2 };
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
));
509 Offset24 clipBoxOffset
;
511 Rect
GetRect(const PaintState
& aState
) const {
512 uint32_t offset
= aState
.mHeader
.v1
->clipListOffset
+ clipBoxOffset
;
513 const auto* box
= aState
.COLRv1BaseAddr() + offset
;
516 return reinterpret_cast<const ClipBoxFormat1
*>(box
)->GetRect(aState
);
518 return reinterpret_cast<const ClipBoxFormat2
*>(box
)->GetRect(aState
);
520 // unknown ClipBoxFormat
525 bool Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const;
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
) {
542 return start
> glyphId
? -1 : 1;
544 return reinterpret_cast<const Clip
*>(bsearch((void*)(uintptr_t)aGlyphId
,
545 clips(), uint32_t(numClips
),
546 sizeof(Clip
), compare
));
548 bool Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const;
553 uint16 paletteEntryIndex
;
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
);
567 struct BaseGlyphRecord
{
569 uint16 firstLayerIndex
;
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");
580 const auto* layers
= aState
.mHeader
.v0
->GetLayerRecords();
581 while (layerIndex
< end
) {
582 if (!layers
[layerIndex
].Paint(aState
, aAlpha
, aPoint
)) {
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
; }
601 struct VarColorStop
: public ColorStop
{
604 float GetStopOffset(const PaintState
& aState
) const {
605 return ApplyVariation(aState
, stopOffset
, varIndexBase
);
607 float GetAlpha(const PaintState
& aState
) const {
608 return ApplyVariation(aState
, alpha
, SatAdd(varIndexBase
, 1));
612 template <typename T
>
614 enum { EXTEND_PAD
= 0, EXTEND_REPEAT
= 1, EXTEND_REFLECT
= 2 };
617 const T
* colorStops() const { return reinterpret_cast<const T
*>(this + 1); }
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) {
624 const auto* stop
= colorStops();
625 return MakeUnique
<ColorPattern
>(
626 aState
.GetColor(stop
->GetPaletteIndex(), stop
->GetAlpha(aState
)));
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
;
641 const auto* stop
= colorStops();
642 if (reinterpret_cast<const char*>(stop
) + count
* sizeof(T
) >
643 aState
.COLRv1BaseAddr() + aState
.mCOLRLength
) {
646 aStops
.SetCapacity(count
);
647 for (uint16_t i
= 0; i
< count
; ++i
, ++stop
) {
649 aState
.GetColor(stop
->GetPaletteIndex(), stop
->GetAlpha(aState
));
650 aStops
.AppendElement(GradientStop
{stop
->GetStopOffset(aState
), color
});
653 *aFirstStop
= *aLastStop
= aStops
[0].offset
;
658 float a
= aStops
[0].offset
;
659 float b
= aStops
.LastElement().offset
;
661 for (auto& gs
: aStops
) {
662 gs
.offset
= a
+ b
- gs
.offset
;
665 // Normalize stops to the range 0.0 .. 1.0, and return the original
667 // Note that if all stops are at the same offset, no normalization
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
;
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
{
687 return ExtendMode::REPEAT
;
689 return ExtendMode::REFLECT
;
692 return ExtendMode::CLAMP
;
695 return aState
.mDrawTarget
->CreateGradientStops(
696 aStops
.Elements(), aStops
.Length(), mapExtendMode(extend
));
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()) {
707 return MakeGradientStops(aState
, stops
);
711 using ColorLine
= ColorLineT
<ColorStop
>;
712 using VarColorLine
= ColorLineT
<VarColorStop
>;
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)) { \
722 aState.mVisited->AppendElement(aOffset); \
723 ScopeExit e([aState]() { aState.mVisited->RemoveLastElement(); })
725 struct PaintColrLayers
{
726 enum { kFormat
= 1 };
729 uint32 firstLayerIndex
;
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();
739 if (uint64_t(firstLayerIndex
) + numLayers
> layerList
->numLayers
) {
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
],
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();
760 if (uint64_t(firstLayerIndex
) + numLayers
> layerList
->numLayers
) {
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
]));
773 struct PaintPatternBase
{
774 bool Paint(const PaintState
& aState
, uint32_t aOffset
,
775 const Rect
* aBounds
) const {
776 Matrix m
= aState
.mDrawTarget
->GetTransform();
778 if (auto pattern
= DispatchMakePattern(aState
, aOffset
)) {
779 aState
.mDrawTarget
->FillRect(
780 m
.TransformBounds(IntRectToRect(aState
.mDrawTarget
->GetRect())),
781 *pattern
, aState
.mDrawOptions
);
788 Rect
GetBoundingRect(const PaintState
& aState
, uint32_t aOffset
) const {
793 struct PaintSolid
: public PaintPatternBase
{
794 enum { kFormat
= 2 };
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
));
806 struct PaintVarSolid
: public PaintSolid
{
807 enum { kFormat
= 3 };
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
)));
818 struct PaintLinearGradient
: public PaintPatternBase
{
819 enum { kFormat
= 4 };
821 Offset24 colorLineOffset
;
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
) {
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
);
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());
852 UniquePtr
<Pattern
> solidColor
= aColorLine
->AsSolidColor(aState
);
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());
862 if (firstStop
!= 0.0 || lastStop
!= 1.0) {
863 if (firstStop
== lastStop
) {
864 if (aColorLine
->extend
!= T::EXTEND_PAD
) {
865 return MakeUnique
<ColorPattern
>(DeviceColor());
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
) {
873 stopArray
.AppendElement(
874 GradientStop
{1.0f
, stopArray
.LastElement().color
});
877 // Adjust positions of the points to account for normalization of the
878 // color line stop offsets.
881 p1
-= v
* (1.0f
- lastStop
);
882 // Move the rotation vector to maintain the same direction from p0.
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
);
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
);
901 RefPtr stops
= aColorLine
->MakeGradientStops(aState
, stopArray
);
902 return MakeUnique
<LinearGradientPattern
>(p0
, p3
, std::move(stops
),
903 Matrix::Scaling(1.0, -1.0));
907 struct PaintVarLinearGradient
: public PaintLinearGradient
{
908 enum { kFormat
= 5 };
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
) {
919 const auto* colorLine
= reinterpret_cast<const VarColorLine
*>(
920 aState
.COLRv1BaseAddr() + clOffset
);
921 Point
p0(aState
.F2P(ApplyVariation(aState
, int16_t(x0
), varIndexBase
)),
923 ApplyVariation(aState
, int16_t(y0
), SatAdd(varIndexBase
, 1))));
925 ApplyVariation(aState
, int16_t(x1
), SatAdd(varIndexBase
, 2))),
927 ApplyVariation(aState
, int16_t(y1
), SatAdd(varIndexBase
, 3))));
929 ApplyVariation(aState
, int16_t(x2
), SatAdd(varIndexBase
, 4))),
931 ApplyVariation(aState
, int16_t(y2
), SatAdd(varIndexBase
, 5))));
932 return NormalizeAndMakeGradient(aState
, colorLine
, p0
, p1
, p2
);
936 struct PaintRadialGradient
: public PaintPatternBase
{
937 enum { kFormat
= 6 };
939 Offset24 colorLineOffset
;
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
) {
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
);
963 // Helper function to trim the gradient stops array at the start or end.
964 void TruncateGradientStops(nsTArray
<GradientStop
>& aStops
, float aStart
,
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!");
973 // Create a color that is |r| of the way from c1 to c2.
974 auto interpolateColor
= [](DeviceColor c1
, DeviceColor c2
, float r
) {
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
));
980 size_t count
= aStops
.Length();
981 MOZ_ASSERT(count
> 1);
983 // Truncate at the start of the color line?
985 // Skip forward past any stops that can be dropped.
987 while (i
< count
- 1 && aStops
[i
].offset
< aStart
) {
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.
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
);
1010 // Truncate at the end of the color line?
1012 // Skip back over any stops that can be dropped.
1013 size_t i
= count
- 1;
1014 while (i
&& aStops
[i
].offset
> aEnd
) {
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
);
1027 aStops
.RemoveElementsAt(i
+ 1, count
- i
- 1);
1028 // Re-normalize the remaining stops to the [0, 1] range.
1030 float r
= 1.0f
/ aEnd
;
1031 for (auto& gs
: aStops
) {
1032 gs
.offset
= r
* gs
.offset
;
1038 template <typename T
>
1039 UniquePtr
<Pattern
> NormalizeAndMakeGradient(const PaintState
& aState
,
1040 const T
* aColorLine
, Point c1
,
1043 if ((c1
== c2
&& r1
== r2
) || (r1
== 0.0 && r2
== 0.0)) {
1044 return MakeUnique
<ColorPattern
>(DeviceColor());
1046 UniquePtr
<Pattern
> solidColor
= aColorLine
->AsSolidColor(aState
);
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());
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());
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
) {
1070 stopArray
.AppendElement(
1071 GradientStop
{1.0f
, stopArray
.LastElement().color
});
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
);
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).
1092 return MakeUnique
<ColorPattern
>(DeviceColor());
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.
1097 // Keep only the last color stop.
1098 stopArray
.RemoveElementsAt(0, stopArray
.Length() - 1);
1100 // Keep only the first color stop.
1101 stopArray
.RemoveElementsAt(1, stopArray
.Length() - 1);
1104 // Truncate the gradient at the tip of the visible cone: find the color
1105 // stops closest to that point and interpolate between them.
1107 float start
= r1
/ (r1
- r2
);
1108 TruncateGradientStops(stopArray
, start
, 1.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
);
1115 c2
= c1
* (1.0f
- end
) + c2
* end
;
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());
1129 Point vec
= c2
- c1
;
1130 if (aColorLine
->extend
== T::EXTEND_REFLECT
) {
1138 // Number of repeats by which we need to shift.
1139 float n
= std::ceil(std::max(-r1
, -r2
) / deltaR
);
1147 RefPtr stops
= aColorLine
->MakeGradientStops(aState
, stopArray
);
1149 return MakeUnique
<ColorPattern
>(DeviceColor());
1151 return MakeUnique
<RadialGradientPattern
>(c1
, c2
, r1
, r2
, std::move(stops
),
1152 Matrix::Scaling(1.0, -1.0));
1156 struct PaintVarRadialGradient
: public PaintRadialGradient
{
1157 enum { kFormat
= 7 };
1158 uint32 varIndexBase
;
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
) {
1168 const auto* colorLine
= reinterpret_cast<const VarColorLine
*>(
1169 aState
.COLRv1BaseAddr() + clOffset
);
1170 Point
c1(aState
.F2P(ApplyVariation(aState
, int16_t(x0
), varIndexBase
)),
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))),
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
);
1185 struct PaintSweepGradient
: public PaintPatternBase
{
1186 enum { kFormat
= 8 };
1188 Offset24 colorLineOffset
;
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
) {
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
);
1209 template <typename T
>
1210 UniquePtr
<Pattern
> NormalizeAndMakeGradient(const PaintState
& aState
,
1211 const T
* aColorLine
,
1212 Point aCenter
, float aStart
,
1214 if (aStart
== aEnd
&& aColorLine
->extend
!= T::EXTEND_PAD
) {
1215 return MakeUnique
<ColorPattern
>(DeviceColor());
1217 UniquePtr
<Pattern
> solidColor
= aColorLine
->AsSolidColor(aState
);
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
;
1227 aColorLine
->MakeGradientStops(aState
, &firstStop
, &lastStop
, reverse
);
1231 if (firstStop
!= 0.0 || lastStop
!= 1.0) {
1232 if (firstStop
== lastStop
) {
1233 if (aColorLine
->extend
!= T::EXTEND_PAD
) {
1234 return MakeUnique
<ColorPattern
>(DeviceColor());
1237 float sweep
= aEnd
- aStart
;
1238 aStart
= aStart
+ sweep
* firstStop
;
1239 aEnd
= aStart
+ sweep
* (lastStop
- firstStop
);
1243 std::swap(aStart
, aEnd
);
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));
1251 struct PaintVarSweepGradient
: public PaintSweepGradient
{
1252 enum { kFormat
= 9 };
1253 uint32 varIndexBase
;
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
) {
1263 const auto* colorLine
= reinterpret_cast<const VarColorLine
*>(
1264 aState
.COLRv1BaseAddr() + clOffset
);
1266 ApplyVariation(aState
, startAngle
, SatAdd(varIndexBase
, 2)) + 1.0f
;
1268 ApplyVariation(aState
, endAngle
, SatAdd(varIndexBase
, 3)) + 1.0f
;
1270 aState
.F2P(ApplyVariation(aState
, int16_t(centerX
), varIndexBase
)),
1272 ApplyVariation(aState
, int16_t(centerY
), SatAdd(varIndexBase
, 1))));
1273 return NormalizeAndMakeGradient(aState
, colorLine
, center
, start
, end
);
1278 enum { kFormat
= 10 };
1280 Offset24 paintOffset
;
1283 bool Paint(const PaintState
& aState
, uint32_t aOffset
,
1284 const Rect
* aBounds
) const {
1285 MOZ_ASSERT(format
== kFormat
);
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
1294 UniquePtr
<Pattern
> fillPattern
=
1295 DispatchMakePattern(aState
, aOffset
+ paintOffset
);
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.
1302 aState
.mScaledFont
->GetPathForGlyphs(buffer
, aState
.mDrawTarget
);
1303 aState
.mDrawTarget
->Fill(path
, *fillPattern
, aState
.mDrawOptions
);
1305 aState
.mDrawTarget
->FillGlyphs(aState
.mScaledFont
, buffer
, *fillPattern
,
1306 aState
.mDrawOptions
);
1311 aState
.mScaledFont
->GetPathForGlyphs(buffer
, aState
.mDrawTarget
);
1312 aState
.mDrawTarget
->PushClip(path
);
1313 bool ok
= DispatchPaint(aState
, aOffset
+ paintOffset
, aBounds
);
1314 aState
.mDrawTarget
->PopClip();
1318 Rect
GetBoundingRect(const PaintState
& aState
, uint32_t aOffset
) const {
1319 MOZ_ASSERT(format
== kFormat
);
1320 Glyph g
{uint16_t(glyphID
), Point()};
1321 GlyphBuffer buffer
{&g
, 1};
1323 aState
.mScaledFont
->GetPathForGlyphs(buffer
, aState
.mDrawTarget
);
1324 return path
->GetFastBounds();
1328 struct PaintColrGlyph
{
1329 enum { kFormat
= 11 };
1333 // Factored out as a helper because this is also used by the top-level
1334 // PaintGlyphGraph function.
1335 static bool DoPaint(const PaintState
& aState
,
1336 const BaseGlyphPaintRecord
* aBaseGlyphPaint
,
1337 uint32_t aGlyphId
, const Rect
* aBounds
) {
1338 AutoPopClips
clips(aState
.mDrawTarget
);
1340 if (const auto* clipList
= aState
.mHeader
.v1
->clipList()) {
1341 if (const auto* clip
= clipList
->GetClip(aGlyphId
)) {
1342 clipRect
= clip
->GetRect(aState
);
1343 clips
.PushClipRect(clipRect
);
1345 aBounds
= &clipRect
;
1349 return DispatchPaint(
1351 aState
.mHeader
.v1
->baseGlyphListOffset
+ aBaseGlyphPaint
->paintOffset
,
1355 bool Paint(const PaintState
& aState
, uint32_t aOffset
,
1356 const Rect
* aBounds
) const {
1357 MOZ_ASSERT(format
== kFormat
);
1358 IF_CYCLE_RETURN(true);
1359 const auto* base
= aState
.mHeader
.v1
->GetBaseGlyphPaint(glyphID
);
1360 return base
? DoPaint(aState
, base
, uint16_t(glyphID
), aBounds
) : false;
1363 Rect
GetBoundingRect(const PaintState
& aState
, uint32_t aOffset
) const {
1364 IF_CYCLE_RETURN(Rect());
1365 if (const auto* clipList
= aState
.mHeader
.v1
->clipList()) {
1366 if (const auto* clip
= clipList
->GetClip(uint16_t(glyphID
))) {
1367 return clip
->GetRect(aState
);
1370 if (const auto* base
=
1371 aState
.mHeader
.v1
->GetBaseGlyphPaint(uint16_t(glyphID
))) {
1372 return DispatchGetBounds(
1373 aState
, aState
.mHeader
.v1
->baseGlyphListOffset
+ base
->paintOffset
);
1379 #undef IF_CYCLE_RETURN
1389 Matrix
AsMatrix(const PaintState
& aState
) const {
1390 // Flip signs because of opposite y-axis direction in moz2d vs opentype.
1391 return Matrix(float(xx
), -float(yx
), -float(xy
), float(yy
),
1392 aState
.F2P(float(dx
)), -aState
.F2P(float(dy
)));
1396 struct VarAffine2x3
: public Affine2x3
{
1397 uint32 varIndexBase
;
1399 Matrix
AsMatrix(const PaintState
& aState
) const {
1401 ApplyVariation(aState
, xx
, varIndexBase
),
1402 -ApplyVariation(aState
, yx
, SatAdd(varIndexBase
, 1)),
1403 -ApplyVariation(aState
, xy
, SatAdd(varIndexBase
, 2)),
1404 ApplyVariation(aState
, yy
, SatAdd(varIndexBase
, 3)),
1405 aState
.F2P(ApplyVariation(aState
, dx
, SatAdd(varIndexBase
, 4))),
1406 -aState
.F2P(ApplyVariation(aState
, dy
, SatAdd(varIndexBase
, 5))));
1410 struct PaintTransformBase
{
1412 Offset24 paintOffset
;
1414 bool Paint(const PaintState
& aState
, uint32_t aOffset
,
1415 const Rect
* aBounds
) const {
1419 AutoRestoreTransform
saveTransform(aState
.mDrawTarget
);
1420 aState
.mDrawTarget
->ConcatTransform(DispatchGetMatrix(aState
, aOffset
));
1421 return DispatchPaint(aState
, aOffset
+ paintOffset
, aBounds
);
1424 Rect
GetBoundingRect(const PaintState
& aState
, uint32_t aOffset
) const {
1428 Rect bounds
= DispatchGetBounds(aState
, aOffset
+ paintOffset
);
1429 bounds
= DispatchGetMatrix(aState
, aOffset
).TransformBounds(bounds
);
1434 struct PaintTransform
: public PaintTransformBase
{
1435 enum { kFormat
= 12 };
1436 Offset24 transformOffset
;
1438 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1439 MOZ_ASSERT(format
== kFormat
);
1440 if (aOffset
+ transformOffset
+ sizeof(Affine2x3
) > aState
.mCOLRLength
) {
1443 const auto* t
= reinterpret_cast<const Affine2x3
*>(
1444 aState
.COLRv1BaseAddr() + aOffset
+ transformOffset
);
1445 return t
->AsMatrix(aState
);
1449 struct PaintVarTransform
: public PaintTransformBase
{
1450 enum { kFormat
= 13 };
1451 Offset24 transformOffset
;
1453 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1454 MOZ_ASSERT(format
== kFormat
);
1455 if (aOffset
+ transformOffset
+ sizeof(VarAffine2x3
) > aState
.mCOLRLength
) {
1458 const auto* t
= reinterpret_cast<const VarAffine2x3
*>(
1459 aState
.COLRv1BaseAddr() + aOffset
+ transformOffset
);
1460 return t
->AsMatrix(aState
);
1464 struct PaintTranslate
: public PaintTransformBase
{
1465 enum { kFormat
= 14 };
1469 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1470 MOZ_ASSERT(format
== kFormat
);
1471 return Matrix::Translation(aState
.F2P(int16_t(dx
)),
1472 -aState
.F2P(int16_t(dy
)));
1476 struct PaintVarTranslate
: public PaintTranslate
{
1477 enum { kFormat
= 15 };
1478 uint32 varIndexBase
;
1480 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1481 MOZ_ASSERT(format
== kFormat
);
1482 return Matrix::Translation(
1483 aState
.F2P(ApplyVariation(aState
, int16_t(dx
), varIndexBase
)),
1485 ApplyVariation(aState
, int16_t(dy
), SatAdd(varIndexBase
, 1))));
1489 struct PaintScale
: public PaintTransformBase
{
1490 enum { kFormat
= 16 };
1494 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1495 MOZ_ASSERT(format
== kFormat
);
1496 return Matrix::Scaling(float(scaleX
), float(scaleY
));
1500 struct PaintVarScale
: public PaintScale
{
1501 enum { kFormat
= 17 };
1502 uint32 varIndexBase
;
1504 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1505 MOZ_ASSERT(format
== kFormat
);
1506 return Matrix::Scaling(
1507 ApplyVariation(aState
, scaleX
, varIndexBase
),
1508 ApplyVariation(aState
, scaleY
, SatAdd(varIndexBase
, 1)));
1512 struct PaintScaleAroundCenter
: public PaintTransformBase
{
1513 enum { kFormat
= 18 };
1519 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1520 MOZ_ASSERT(format
== kFormat
);
1521 Point
center(aState
.F2P(int16_t(centerX
)), -aState
.F2P(int16_t(centerY
)));
1522 return Matrix::Translation(center
)
1523 .PreScale(float(scaleX
), float(scaleY
))
1524 .PreTranslate(-center
);
1528 struct PaintVarScaleAroundCenter
: public PaintScaleAroundCenter
{
1529 enum { kFormat
= 19 };
1530 uint32 varIndexBase
;
1532 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1533 MOZ_ASSERT(format
== kFormat
);
1534 Point
center(aState
.F2P(ApplyVariation(aState
, int16_t(centerX
),
1535 SatAdd(varIndexBase
, 2))),
1536 -aState
.F2P(ApplyVariation(aState
, int16_t(centerY
),
1537 SatAdd(varIndexBase
, 3))));
1538 return Matrix::Translation(center
)
1539 .PreScale(ApplyVariation(aState
, scaleX
, varIndexBase
),
1540 ApplyVariation(aState
, scaleY
, SatAdd(varIndexBase
, 1)))
1541 .PreTranslate(-center
);
1545 struct PaintScaleUniform
: public PaintTransformBase
{
1546 enum { kFormat
= 20 };
1549 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1550 MOZ_ASSERT(format
== kFormat
);
1551 return Matrix::Scaling(float(scale
), float(scale
));
1555 struct PaintVarScaleUniform
: public PaintScaleUniform
{
1556 enum { kFormat
= 21 };
1557 uint32 varIndexBase
;
1559 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1560 MOZ_ASSERT(format
== kFormat
);
1561 float sc
= ApplyVariation(aState
, scale
, varIndexBase
);
1562 return Matrix::Scaling(sc
, sc
);
1566 struct PaintScaleUniformAroundCenter
: public PaintTransformBase
{
1567 enum { kFormat
= 22 };
1572 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1573 MOZ_ASSERT(format
== kFormat
);
1574 Point
center(aState
.F2P(int16_t(centerX
)), -aState
.F2P(int16_t(centerY
)));
1575 return Matrix::Translation(center
)
1576 .PreScale(float(scale
), float(scale
))
1577 .PreTranslate(-center
);
1581 struct PaintVarScaleUniformAroundCenter
: public PaintScaleUniformAroundCenter
{
1582 enum { kFormat
= 23 };
1583 uint32 varIndexBase
;
1585 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1586 MOZ_ASSERT(format
== kFormat
);
1587 float sc
= ApplyVariation(aState
, scale
, varIndexBase
);
1588 Point
center(aState
.F2P(ApplyVariation(aState
, int16_t(centerX
),
1589 SatAdd(varIndexBase
, 1))),
1590 -aState
.F2P(ApplyVariation(aState
, int16_t(centerY
),
1591 SatAdd(varIndexBase
, 2))));
1592 return Matrix::Translation(center
).PreScale(sc
, sc
).PreTranslate(-center
);
1596 struct PaintRotate
: public PaintTransformBase
{
1597 enum { kFormat
= 24 };
1600 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1601 MOZ_ASSERT(format
== kFormat
);
1602 return Matrix::Rotation(-float(angle
) * float(M_PI
));
1606 struct PaintVarRotate
: public PaintRotate
{
1607 enum { kFormat
= 25 };
1608 uint32 varIndexBase
;
1610 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1611 MOZ_ASSERT(format
== kFormat
);
1612 float ang
= ApplyVariation(aState
, angle
, varIndexBase
);
1613 return Matrix::Rotation(-ang
* float(M_PI
));
1617 struct PaintRotateAroundCenter
: public PaintTransformBase
{
1618 enum { kFormat
= 26 };
1623 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1624 MOZ_ASSERT(format
== kFormat
);
1625 Point
center(aState
.F2P(int16_t(centerX
)), -aState
.F2P(int16_t(centerY
)));
1626 return Matrix::Translation(center
)
1627 .PreRotate(-float(angle
) * float(M_PI
))
1628 .PreTranslate(-center
);
1632 struct PaintVarRotateAroundCenter
: public PaintRotateAroundCenter
{
1633 enum { kFormat
= 27 };
1634 uint32 varIndexBase
;
1636 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1637 MOZ_ASSERT(format
== kFormat
);
1638 float ang
= ApplyVariation(aState
, angle
, varIndexBase
);
1639 Point
center(aState
.F2P(ApplyVariation(aState
, int16_t(centerX
),
1640 SatAdd(varIndexBase
, 1))),
1641 -aState
.F2P(ApplyVariation(aState
, int16_t(centerY
),
1642 SatAdd(varIndexBase
, 2))));
1643 return Matrix::Translation(center
)
1644 .PreRotate(-ang
* float(M_PI
))
1645 .PreTranslate(-center
);
1649 static inline Matrix
SkewMatrix(float aSkewX
, float aSkewY
) {
1650 float xy
= tanf(aSkewX
* float(M_PI
));
1651 float yx
= tanf(aSkewY
* float(M_PI
));
1652 return std::isnan(xy
) || std::isnan(yx
) ? Matrix()
1653 : Matrix(1.0, -yx
, xy
, 1.0, 0.0, 0.0);
1656 struct PaintSkew
: public PaintTransformBase
{
1657 enum { kFormat
= 28 };
1661 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1662 MOZ_ASSERT(format
== kFormat
);
1663 return SkewMatrix(float(xSkewAngle
), float(ySkewAngle
));
1667 struct PaintVarSkew
: public PaintSkew
{
1668 enum { kFormat
= 29 };
1669 uint32 varIndexBase
;
1671 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1672 MOZ_ASSERT(format
== kFormat
);
1674 float(ApplyVariation(aState
, xSkewAngle
, varIndexBase
)),
1675 float(ApplyVariation(aState
, ySkewAngle
, SatAdd(varIndexBase
, 1))));
1679 struct PaintSkewAroundCenter
: public PaintTransformBase
{
1680 enum { kFormat
= 30 };
1686 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1687 MOZ_ASSERT(format
== kFormat
);
1688 Point
center(aState
.F2P(int16_t(centerX
)), -aState
.F2P(int16_t(centerY
)));
1689 return Matrix::Translation(center
)
1690 .PreMultiply(SkewMatrix(float(xSkewAngle
), float(ySkewAngle
)))
1691 .PreTranslate(-center
);
1695 struct PaintVarSkewAroundCenter
: public PaintSkewAroundCenter
{
1696 enum { kFormat
= 31 };
1697 uint32 varIndexBase
;
1699 Matrix
GetMatrix(const PaintState
& aState
, uint32_t aOffset
) const {
1700 MOZ_ASSERT(format
== kFormat
);
1701 Point
center(aState
.F2P(ApplyVariation(aState
, int16_t(centerX
),
1702 SatAdd(varIndexBase
, 2))),
1703 -aState
.F2P(ApplyVariation(aState
, int16_t(centerY
),
1704 SatAdd(varIndexBase
, 3))));
1705 return Matrix::Translation(center
)
1706 .PreMultiply(SkewMatrix(
1707 ApplyVariation(aState
, xSkewAngle
, varIndexBase
),
1708 ApplyVariation(aState
, ySkewAngle
, SatAdd(varIndexBase
, 1))))
1709 .PreTranslate(-center
);
1713 struct PaintComposite
{
1714 enum { kFormat
= 32 };
1716 Offset24 sourcePaintOffset
;
1717 uint8_t compositeMode
;
1718 Offset24 backdropPaintOffset
;
1721 COMPOSITE_CLEAR
= 0,
1724 COMPOSITE_SRC_OVER
= 3,
1725 COMPOSITE_DEST_OVER
= 4,
1726 COMPOSITE_SRC_IN
= 5,
1727 COMPOSITE_DEST_IN
= 6,
1728 COMPOSITE_SRC_OUT
= 7,
1729 COMPOSITE_DEST_OUT
= 8,
1730 COMPOSITE_SRC_ATOP
= 9,
1731 COMPOSITE_DEST_ATOP
= 10,
1733 COMPOSITE_PLUS
= 12,
1734 COMPOSITE_SCREEN
= 13,
1735 COMPOSITE_OVERLAY
= 14,
1736 COMPOSITE_DARKEN
= 15,
1737 COMPOSITE_LIGHTEN
= 16,
1738 COMPOSITE_COLOR_DODGE
= 17,
1739 COMPOSITE_COLOR_BURN
= 18,
1740 COMPOSITE_HARD_LIGHT
= 19,
1741 COMPOSITE_SOFT_LIGHT
= 20,
1742 COMPOSITE_DIFFERENCE
= 21,
1743 COMPOSITE_EXCLUSION
= 22,
1744 COMPOSITE_MULTIPLY
= 23,
1745 COMPOSITE_HSL_HUE
= 24,
1746 COMPOSITE_HSL_SATURATION
= 25,
1747 COMPOSITE_HSL_COLOR
= 26,
1748 COMPOSITE_HSL_LUMINOSITY
= 27
1751 bool Paint(const PaintState
& aState
, uint32_t aOffset
,
1752 const Rect
* aBounds
) const {
1753 MOZ_ASSERT(format
== kFormat
);
1754 if (!backdropPaintOffset
|| !sourcePaintOffset
) {
1757 auto mapCompositionMode
= [](uint8_t aMode
) -> CompositionOp
{
1760 return CompositionOp::OP_SOURCE
;
1761 case COMPOSITE_CLEAR
:
1763 case COMPOSITE_DEST
:
1764 MOZ_ASSERT_UNREACHABLE("should have short-circuited");
1765 return CompositionOp::OP_SOURCE
;
1766 case COMPOSITE_SRC_OVER
:
1767 return CompositionOp::OP_OVER
;
1768 case COMPOSITE_DEST_OVER
:
1769 return CompositionOp::OP_DEST_OVER
;
1770 case COMPOSITE_SRC_IN
:
1771 return CompositionOp::OP_IN
;
1772 case COMPOSITE_DEST_IN
:
1773 return CompositionOp::OP_DEST_IN
;
1774 case COMPOSITE_SRC_OUT
:
1775 return CompositionOp::OP_OUT
;
1776 case COMPOSITE_DEST_OUT
:
1777 return CompositionOp::OP_DEST_OUT
;
1778 case COMPOSITE_SRC_ATOP
:
1779 return CompositionOp::OP_ATOP
;
1780 case COMPOSITE_DEST_ATOP
:
1781 return CompositionOp::OP_DEST_ATOP
;
1783 return CompositionOp::OP_XOR
;
1784 case COMPOSITE_PLUS
:
1785 return CompositionOp::OP_ADD
;
1786 case COMPOSITE_SCREEN
:
1787 return CompositionOp::OP_SCREEN
;
1788 case COMPOSITE_OVERLAY
:
1789 return CompositionOp::OP_OVERLAY
;
1790 case COMPOSITE_DARKEN
:
1791 return CompositionOp::OP_DARKEN
;
1792 case COMPOSITE_LIGHTEN
:
1793 return CompositionOp::OP_LIGHTEN
;
1794 case COMPOSITE_COLOR_DODGE
:
1795 return CompositionOp::OP_COLOR_DODGE
;
1796 case COMPOSITE_COLOR_BURN
:
1797 return CompositionOp::OP_COLOR_BURN
;
1798 case COMPOSITE_HARD_LIGHT
:
1799 return CompositionOp::OP_HARD_LIGHT
;
1800 case COMPOSITE_SOFT_LIGHT
:
1801 return CompositionOp::OP_SOFT_LIGHT
;
1802 case COMPOSITE_DIFFERENCE
:
1803 return CompositionOp::OP_DIFFERENCE
;
1804 case COMPOSITE_EXCLUSION
:
1805 return CompositionOp::OP_EXCLUSION
;
1806 case COMPOSITE_MULTIPLY
:
1807 return CompositionOp::OP_MULTIPLY
;
1808 case COMPOSITE_HSL_HUE
:
1809 return CompositionOp::OP_HUE
;
1810 case COMPOSITE_HSL_SATURATION
:
1811 return CompositionOp::OP_SATURATION
;
1812 case COMPOSITE_HSL_COLOR
:
1813 return CompositionOp::OP_COLOR
;
1814 case COMPOSITE_HSL_LUMINOSITY
:
1815 return CompositionOp::OP_LUMINOSITY
;
1818 // Short-circuit cases:
1819 if (compositeMode
== COMPOSITE_CLEAR
) {
1822 if (compositeMode
== COMPOSITE_SRC
) {
1823 return DispatchPaint(aState
, aOffset
+ sourcePaintOffset
, aBounds
);
1825 if (compositeMode
== COMPOSITE_DEST
) {
1826 return DispatchPaint(aState
, aOffset
+ backdropPaintOffset
, aBounds
);
1829 // We need bounds for the temporary surfaces; so if we didn't have
1830 // explicitly-provided bounds from a clipList entry for the top-level
1831 // glyph, then we need to determine the bounding rect here.
1832 Rect bounds
= aBounds
? *aBounds
: GetBoundingRect(aState
, aOffset
);
1833 if (bounds
.IsEmpty()) {
1838 PaintState state
= aState
;
1839 state
.mDrawOptions
.mCompositionOp
= CompositionOp::OP_OVER
;
1840 IntSize
intSize(int(bounds
.width
), int(bounds
.height
));
1842 if (!aState
.mDrawTarget
->CanCreateSimilarDrawTarget(
1843 intSize
, SurfaceFormat::B8G8R8A8
)) {
1844 // We're not going to be able to render this, so just bail out.
1845 // (Returning true rather than false means we'll just not paint this
1846 // part of the glyph, but won't return an error and likely fall back
1847 // to an ugly black blob.)
1851 // Draw the backdrop paint graph to a temporary surface.
1852 RefPtr backdrop
= aState
.mDrawTarget
->CreateSimilarDrawTarget(
1853 intSize
, SurfaceFormat::B8G8R8A8
);
1857 backdrop
->SetTransform(Matrix::Translation(-bounds
.TopLeft()));
1858 state
.mDrawTarget
= backdrop
;
1859 if (!DispatchPaint(state
, aOffset
+ backdropPaintOffset
, &bounds
)) {
1863 // Draw the source paint graph to another temp surface.
1864 RefPtr source
= aState
.mDrawTarget
->CreateSimilarDrawTarget(
1865 intSize
, SurfaceFormat::B8G8R8A8
);
1869 source
->SetTransform(Matrix::Translation(-bounds
.TopLeft()));
1870 state
.mDrawTarget
= source
;
1871 if (!DispatchPaint(state
, aOffset
+ sourcePaintOffset
, &bounds
)) {
1875 // Composite the source onto the backdrop using the specified operation.
1876 Rect
localBounds(Point(), bounds
.Size());
1877 RefPtr snapshot
= source
->Snapshot();
1878 backdrop
->SetTransform(Matrix());
1879 backdrop
->DrawSurface(snapshot
, localBounds
, localBounds
,
1880 DrawSurfaceOptions(),
1881 DrawOptions(1.0, mapCompositionMode(compositeMode
)));
1883 // And copy the composited result to our final destination.
1884 snapshot
= backdrop
->Snapshot();
1885 aState
.mDrawTarget
->DrawSurface(snapshot
, bounds
, localBounds
);
1890 Rect
GetBoundingRect(const PaintState
& aState
, uint32_t aOffset
) const {
1891 if (!backdropPaintOffset
|| !sourcePaintOffset
) {
1894 // For now, just return the maximal bounds that could result; this could be
1895 // smarter, returning just one of the rects or their intersection when
1896 // appropriate for the composite mode in effect.
1897 return DispatchGetBounds(aState
, aOffset
+ backdropPaintOffset
)
1898 .Union(DispatchGetBounds(aState
, aOffset
+ sourcePaintOffset
));
1904 const BaseGlyphPaintRecord
* COLRv1Header::GetBaseGlyphPaint(
1905 uint32_t aGlyphId
) const {
1906 const auto* list
= baseGlyphList();
1910 auto compare
= [](const void* key
, const void* data
) -> int {
1911 uint32_t glyphId
= (uint32_t)(uintptr_t)key
;
1912 const auto* paintRecord
=
1913 reinterpret_cast<const BaseGlyphPaintRecord
*>(data
);
1914 uint32_t baseGlyphId
= uint16_t(paintRecord
->glyphID
);
1915 if (baseGlyphId
== glyphId
) {
1918 return baseGlyphId
> glyphId
? -1 : 1;
1920 return reinterpret_cast<const BaseGlyphPaintRecord
*>(
1921 bsearch((void*)(uintptr_t)aGlyphId
, list
+ 1,
1922 uint32_t(list
->numBaseGlyphPaintRecords
),
1923 sizeof(BaseGlyphPaintRecord
), compare
));
1926 #define DO_CASE_VAR(T) \
1927 DO_CASE(Paint##T); \
1928 DO_CASE(PaintVar##T)
1930 // Process paint table at aOffset from start of COLRv1 table.
1931 static bool DispatchPaint(const PaintState
& aState
, uint32_t aOffset
,
1932 const Rect
* aBounds
) {
1933 if (aOffset
>= aState
.mCOLRLength
) {
1937 const char* paint
= aState
.COLRv1BaseAddr() + aOffset
;
1938 // All paint table formats start with an 8-bit 'format' field.
1939 uint8_t format
= uint8_t(*paint
);
1941 #define DO_CASE(T) \
1943 return aOffset + sizeof(T) <= aState.mCOLRLength \
1944 ? reinterpret_cast<const T*>(paint)->Paint(aState, aOffset, \
1949 DO_CASE(PaintColrLayers
);
1951 DO_CASE_VAR(LinearGradient
);
1952 DO_CASE_VAR(RadialGradient
);
1953 DO_CASE_VAR(SweepGradient
);
1954 DO_CASE(PaintGlyph
);
1955 DO_CASE(PaintColrGlyph
);
1956 DO_CASE_VAR(Transform
);
1957 DO_CASE_VAR(Translate
);
1959 DO_CASE_VAR(ScaleAroundCenter
);
1960 DO_CASE_VAR(ScaleUniform
);
1961 DO_CASE_VAR(ScaleUniformAroundCenter
);
1962 DO_CASE_VAR(Rotate
);
1963 DO_CASE_VAR(RotateAroundCenter
);
1965 DO_CASE_VAR(SkewAroundCenter
);
1966 DO_CASE(PaintComposite
);
1976 // Get a gfx::Pattern corresponding to the given paint table, if it is a
1977 // simple format that can be used as a fill (not a sub-graph).
1978 static UniquePtr
<Pattern
> DispatchMakePattern(const PaintState
& aState
,
1980 if (aOffset
>= aState
.mCOLRLength
) {
1984 const char* paint
= aState
.COLRv1BaseAddr() + aOffset
;
1985 // All paint table formats start with an 8-bit 'format' field.
1986 uint8_t format
= uint8_t(*paint
);
1988 #define DO_CASE(T) \
1990 return aOffset + sizeof(T) <= aState.mCOLRLength \
1991 ? reinterpret_cast<const T*>(paint)->MakePattern(aState, \
1997 DO_CASE_VAR(LinearGradient
);
1998 DO_CASE_VAR(RadialGradient
);
1999 DO_CASE_VAR(SweepGradient
);
2009 static Matrix
DispatchGetMatrix(const PaintState
& aState
, uint32_t aOffset
) {
2010 if (aOffset
>= aState
.mCOLRLength
) {
2014 const char* paint
= aState
.COLRv1BaseAddr() + aOffset
;
2015 // All paint table formats start with an 8-bit 'format' field.
2016 uint8_t format
= uint8_t(*paint
);
2018 #define DO_CASE(T) \
2020 return aOffset + sizeof(T) <= aState.mCOLRLength \
2021 ? reinterpret_cast<const T*>(paint)->GetMatrix(aState, aOffset) \
2025 DO_CASE_VAR(Transform
);
2026 DO_CASE_VAR(Translate
);
2028 DO_CASE_VAR(ScaleAroundCenter
);
2029 DO_CASE_VAR(ScaleUniform
);
2030 DO_CASE_VAR(ScaleUniformAroundCenter
);
2031 DO_CASE_VAR(Rotate
);
2032 DO_CASE_VAR(RotateAroundCenter
);
2034 DO_CASE_VAR(SkewAroundCenter
);
2044 static Rect
DispatchGetBounds(const PaintState
& aState
, uint32_t aOffset
) {
2045 if (aOffset
>= aState
.mCOLRLength
) {
2049 const char* paint
= aState
.COLRv1BaseAddr() + aOffset
;
2050 // All paint table formats start with an 8-bit 'format' field.
2051 uint8_t format
= uint8_t(*paint
);
2053 #define DO_CASE(T) \
2055 return aOffset + sizeof(T) <= aState.mCOLRLength \
2056 ? reinterpret_cast<const T*>(paint)->GetBoundingRect(aState, \
2061 DO_CASE(PaintColrLayers
);
2063 DO_CASE_VAR(LinearGradient
);
2064 DO_CASE_VAR(RadialGradient
);
2065 DO_CASE_VAR(SweepGradient
);
2066 DO_CASE(PaintGlyph
);
2067 DO_CASE(PaintColrGlyph
);
2068 DO_CASE_VAR(Transform
);
2069 DO_CASE_VAR(Translate
);
2071 DO_CASE_VAR(ScaleAroundCenter
);
2072 DO_CASE_VAR(ScaleUniform
);
2073 DO_CASE_VAR(ScaleUniformAroundCenter
);
2074 DO_CASE_VAR(Rotate
);
2075 DO_CASE_VAR(RotateAroundCenter
);
2077 DO_CASE_VAR(SkewAroundCenter
);
2078 DO_CASE(PaintComposite
);
2090 bool COLRHeader::Validate(uint64_t aLength
) const {
2092 if ((count
= numBaseGlyphRecords
)) {
2093 if (baseGlyphRecordsOffset
+ count
* sizeof(BaseGlyphRecord
) > aLength
) {
2097 if ((count
= numLayerRecords
)) {
2098 if (layerRecordsOffset
+ count
* sizeof(LayerRecord
) > aLength
) {
2102 // Check ordering of baseGlyphRecords, and that layer indices are in bounds.
2103 int32_t lastGlyphId
= -1;
2104 const auto* baseGlyph
= reinterpret_cast<const BaseGlyphRecord
*>(
2105 reinterpret_cast<const char*>(this) + baseGlyphRecordsOffset
);
2106 for (uint16_t i
= 0; i
< uint16_t(numBaseGlyphRecords
); i
++, baseGlyph
++) {
2107 uint16_t glyphId
= baseGlyph
->glyphId
;
2108 if (lastGlyphId
>= int32_t(glyphId
)) {
2111 if (uint32_t(baseGlyph
->firstLayerIndex
) + uint32_t(baseGlyph
->numLayers
) >
2112 uint32_t(numLayerRecords
)) {
2115 lastGlyphId
= glyphId
;
2117 // We don't need to validate all the layer paletteEntryIndex fields here,
2118 // because PaintState.GetColor will range-check them at paint time.
2122 bool COLRv1Header::Validate(uint64_t aLength
) const {
2123 if (!base
.Validate(aLength
)) {
2126 if (baseGlyphListOffset
+ sizeof(BaseGlyphList
) > aLength
||
2127 layerListOffset
+ sizeof(LayerList
) > aLength
||
2128 clipListOffset
+ sizeof(ClipList
) > aLength
||
2129 varIndexMapOffset
+ sizeof(DeltaSetIndexMap
) > aLength
||
2130 itemVariationStoreOffset
+ sizeof(ItemVariationStore
) > aLength
) {
2133 const auto* b
= baseGlyphList();
2134 if (b
&& !b
->Validate(this, aLength
)) {
2137 const auto* l
= layerList();
2138 if (l
&& !l
->Validate(this, aLength
)) {
2141 const auto* c
= clipList();
2142 if (c
&& !c
->Validate(this, aLength
)) {
2145 const auto* v
= varIndexMap();
2146 if (v
&& !v
->Validate(this, aLength
)) {
2149 const auto* i
= itemVariationStore();
2150 if (i
&& !i
->Validate(this, aLength
)) {
2156 bool BaseGlyphList::Validate(const COLRv1Header
* aHeader
,
2157 uint64_t aLength
) const {
2158 uint64_t count
= numBaseGlyphPaintRecords
;
2159 if (aHeader
->baseGlyphListOffset
+ sizeof(BaseGlyphList
) +
2160 count
* sizeof(BaseGlyphPaintRecord
) >
2164 // Check ordering of baseGlyphPaint records.
2165 const auto* records
= baseGlyphPaintRecords();
2166 int32_t prevGlyphID
= -1;
2167 for (uint32_t i
= 0; i
< numBaseGlyphPaintRecords
; ++i
) {
2168 const auto& base
= records
[i
];
2169 if (int32_t(uint16_t(base
.glyphID
)) <= prevGlyphID
) {
2172 prevGlyphID
= base
.glyphID
;
2177 bool LayerList::Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const {
2178 // Check that paintOffsets array fits.
2179 uint64_t count
= numLayers
;
2180 uint32_t listOffset
= aHeader
->layerListOffset
;
2181 if (listOffset
+ sizeof(LayerList
) + count
* sizeof(uint32
) > aLength
) {
2184 // Check that values in paintOffsets are within bounds.
2185 const auto* offsets
= paintOffsets();
2186 for (uint32_t i
= 0; i
< count
; i
++) {
2187 if (listOffset
+ offsets
[i
] >= aLength
) {
2194 bool Clip::Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const {
2195 uint32_t offset
= aHeader
->clipListOffset
+ clipBoxOffset
;
2196 if (offset
>= aLength
) {
2199 // ClipBox format begins with a 1-byte format field.
2200 const uint8_t* box
= reinterpret_cast<const uint8_t*>(aHeader
) + offset
;
2203 if (offset
<= aLength
- sizeof(ClipBoxFormat1
)) {
2208 if (offset
<= aLength
- sizeof(ClipBoxFormat2
)) {
2213 // unknown ClipBoxFormat
2219 bool ClipList::Validate(const COLRv1Header
* aHeader
, uint64_t aLength
) const {
2220 uint64_t count
= numClips
;
2221 if (aHeader
->clipListOffset
+ sizeof(ClipList
) + count
* sizeof(Clip
) >
2225 // Check ordering of clip records, and that they are within bounds.
2226 const auto* clipArray
= clips();
2227 int32_t prevEnd
= -1;
2228 for (uint32_t i
= 0; i
< count
; ++i
) {
2229 const auto& clip
= clipArray
[i
];
2230 if (int32_t(uint16_t(clip
.startGlyphID
)) <= prevEnd
) {
2233 if (!clip
.Validate(aHeader
, aLength
)) {
2236 prevEnd
= uint16_t(clip
.endGlyphID
);
2241 bool DeltaSetIndexMap::Validate(const COLRv1Header
* aHeader
,
2242 uint64_t aLength
) const {
2243 uint64_t entrySize
= ((entryFormat
& MAP_ENTRY_SIZE_MASK
) >> 4) + 1;
2248 mapCount
= uint32_t(v0
.mapCount
);
2252 mapCount
= uint32_t(v1
.mapCount
);
2258 if (aHeader
->varIndexMapOffset
+ baseSize
+ mapCount
* entrySize
> aLength
) {
2264 bool ItemVariationStore::Validate(const COLRv1Header
* aHeader
,
2265 uint64_t aLength
) const {
2266 uint64_t offset
= reinterpret_cast<const char*>(this) -
2267 reinterpret_cast<const char*>(aHeader
);
2268 if (offset
+ variationRegionListOffset
+ sizeof(VariationRegionList
) >
2272 if (!variationRegionList()->Validate(aHeader
, aLength
)) {
2275 uint16_t count
= itemVariationDataCount
;
2276 if (offset
+ sizeof(ItemVariationStore
) + count
* sizeof(Offset32
) >
2280 const auto* ivdOffsets
= itemVariationDataOffsets();
2281 for (uint16_t i
= 0; i
< count
; ++i
) {
2282 uint32_t o
= ivdOffsets
[i
];
2283 if (offset
+ o
+ sizeof(ItemVariationData
) > aLength
) {
2286 const auto* variationData
= reinterpret_cast<const ItemVariationData
*>(
2287 reinterpret_cast<const char*>(this) + ivdOffsets
[i
]);
2288 if (!variationData
->Validate(aHeader
, aLength
)) {
2295 bool ItemVariationData::Validate(const COLRv1Header
* aHeader
,
2296 uint64_t aLength
) const {
2297 if (reinterpret_cast<const char*>(regionIndexes() +
2298 uint16_t(regionIndexCount
)) >
2299 reinterpret_cast<const char*>(aHeader
) + aLength
) {
2302 uint16_t wordDeltaCount
= this->wordDeltaCount
;
2303 bool longWords
= wordDeltaCount
& LONG_WORDS
;
2304 wordDeltaCount
&= WORD_DELTA_COUNT_MASK
;
2305 uint32_t deltaSetSize
=
2306 (uint16_t(regionIndexCount
) + uint16_t(wordDeltaCount
)) << longWords
;
2307 if (reinterpret_cast<const char*>(deltaSets()) +
2308 uint16_t(itemCount
) * deltaSetSize
>
2309 reinterpret_cast<const char*>(aHeader
) + aLength
) {
2315 } // end anonymous namespace
2317 namespace mozilla::gfx
{
2319 bool COLRFonts::ValidateColorGlyphs(hb_blob_t
* aCOLR
, hb_blob_t
* aCPAL
) {
2320 struct ColorRecord
{
2327 struct CPALHeaderVersion0
{
2329 uint16 numPaletteEntries
;
2331 uint16 numColorRecords
;
2332 Offset32 colorRecordsArrayOffset
;
2333 // uint16 colorRecordIndices[numPalettes];
2334 const uint16
* colorRecordIndices() const {
2335 return reinterpret_cast<const uint16
*>(this + 1);
2339 unsigned int cpalLength
;
2340 const CPALHeaderVersion0
* cpal
= reinterpret_cast<const CPALHeaderVersion0
*>(
2341 hb_blob_get_data(aCPAL
, &cpalLength
));
2342 if (!cpal
|| cpalLength
< sizeof(CPALHeaderVersion0
)) {
2346 // We can handle either version 0 or 1.
2347 if (uint16_t(cpal
->version
) > 1) {
2351 uint16_t numPaletteEntries
= cpal
->numPaletteEntries
;
2352 uint16_t numPalettes
= cpal
->numPalettes
;
2353 uint16_t numColorRecords
= cpal
->numColorRecords
;
2354 uint32_t colorRecordsArrayOffset
= cpal
->colorRecordsArrayOffset
;
2355 const auto* indices
= cpal
->colorRecordIndices();
2356 if (colorRecordsArrayOffset
>= cpalLength
) {
2359 if (!numPaletteEntries
|| !numPalettes
||
2360 numColorRecords
< numPaletteEntries
) {
2363 if (sizeof(ColorRecord
) * numColorRecords
>
2364 cpalLength
- colorRecordsArrayOffset
) {
2367 if (sizeof(uint16
) * numPalettes
> cpalLength
- sizeof(CPALHeaderVersion0
)) {
2370 for (uint16_t i
= 0; i
< numPalettes
; ++i
) {
2371 uint32_t index
= indices
[i
];
2372 if (index
+ numPaletteEntries
> numColorRecords
) {
2377 // The additional fields in CPALv1 are not checked here; the harfbuzz code
2378 // handles reading them safely.
2380 unsigned int colrLength
;
2381 const COLRHeader
* colr
=
2382 reinterpret_cast<const COLRHeader
*>(hb_blob_get_data(aCOLR
, &colrLength
));
2383 if (!colr
|| colrLength
< sizeof(COLRHeader
)) {
2387 if (uint16_t(colr
->version
) == 1) {
2388 return StaticPrefs::gfx_font_rendering_colr_v1_enabled() &&
2389 colrLength
>= sizeof(COLRv1Header
) &&
2390 reinterpret_cast<const COLRv1Header
*>(colr
)->Validate(colrLength
);
2393 if (uint16_t(colr
->version
) != 0) {
2394 // We only support version 1 (above) or version 0 headers.
2398 return colr
->Validate(colrLength
);
2401 const COLRFonts::GlyphLayers
* COLRFonts::GetGlyphLayers(hb_blob_t
* aCOLR
,
2402 uint32_t aGlyphId
) {
2403 unsigned int length
;
2404 const COLRHeader
* colr
=
2405 reinterpret_cast<const COLRHeader
*>(hb_blob_get_data(aCOLR
, &length
));
2406 // This should never be called unless we have checked that the COLR table is
2407 // structurally valid, so it will be safe to read the header fields.
2408 MOZ_RELEASE_ASSERT(colr
&& length
>= sizeof(COLRHeader
), "bad COLR table!");
2409 auto compareBaseGlyph
= [](const void* key
, const void* data
) -> int {
2410 uint32_t glyphId
= (uint32_t)(uintptr_t)key
;
2411 const auto* baseGlyph
= reinterpret_cast<const BaseGlyphRecord
*>(data
);
2412 uint32_t baseGlyphId
= uint16_t(baseGlyph
->glyphId
);
2413 if (baseGlyphId
== glyphId
) {
2416 return baseGlyphId
> glyphId
? -1 : 1;
2418 return reinterpret_cast<const GlyphLayers
*>(
2419 bsearch((void*)(uintptr_t)aGlyphId
, colr
->GetBaseGlyphRecords(),
2420 uint16_t(colr
->numBaseGlyphRecords
), sizeof(BaseGlyphRecord
),
2424 bool COLRFonts::PaintGlyphLayers(
2425 hb_blob_t
* aCOLR
, hb_face_t
* aFace
, const GlyphLayers
* aLayers
,
2426 DrawTarget
* aDrawTarget
, layout::TextDrawTarget
* aTextDrawer
,
2427 ScaledFont
* aScaledFont
, DrawOptions aDrawOptions
, const Point
& aPoint
,
2428 const sRGBColor
& aCurrentColor
, const nsTArray
<sRGBColor
>* aColors
) {
2429 const auto* glyphRecord
= reinterpret_cast<const BaseGlyphRecord
*>(aLayers
);
2430 // Default to opaque rendering (non-webrender applies alpha with a layer)
2433 // defaultColor is the one that comes from CSS, so it has transparency info.
2434 bool hasComplexTransparency
=
2435 0.0 < aCurrentColor
.a
&& aCurrentColor
.a
< 1.0;
2436 if (hasComplexTransparency
&& uint16_t(glyphRecord
->numLayers
) > 1) {
2437 // WebRender doesn't support drawing multi-layer transparent color-glyphs,
2438 // as it requires compositing all the layers before applying transparency.
2439 // (pretend to succeed, output doesn't matter, we will emit a blob)
2440 aTextDrawer
->FoundUnsupportedFeature();
2444 // If we get here, then either alpha is 0 or 1, or there's only one layer
2445 // which shouldn't have composition issues. In all of these cases, applying
2446 // transparency directly to the glyph should work perfectly fine.
2448 // Note that we must still emit completely transparent emoji, because they
2449 // might be wrapped in a shadow that uses the text run's glyphs.
2450 alpha
= aCurrentColor
.a
;
2453 unsigned int length
;
2454 const COLRHeader
* colr
=
2455 reinterpret_cast<const COLRHeader
*>(hb_blob_get_data(aCOLR
, &length
));
2456 PaintState state
{{colr
},
2457 aColors
->Elements(),
2460 nullptr, // variations not needed
2464 0.0, // fontUnitsToPixels not needed
2465 uint16_t(aColors
->Length()),
2468 return glyphRecord
->Paint(state
, alpha
, aPoint
);
2471 const COLRFonts::GlyphPaintGraph
* COLRFonts::GetGlyphPaintGraph(
2472 hb_blob_t
* aCOLR
, uint32_t aGlyphId
) {
2473 if (!StaticPrefs::gfx_font_rendering_colr_v1_enabled()) {
2476 unsigned int blobLength
;
2478 reinterpret_cast<const COLRHeader
*>(hb_blob_get_data(aCOLR
, &blobLength
));
2479 MOZ_ASSERT(colr
, "Cannot get COLR raw data");
2480 MOZ_ASSERT(blobLength
>= sizeof(COLRHeader
), "COLR data too small");
2482 uint16_t version
= colr
->version
;
2484 MOZ_ASSERT(blobLength
>= sizeof(COLRv1Header
), "COLRv1 data too small");
2485 const auto* colrv1
= reinterpret_cast<const COLRv1Header
*>(colr
);
2486 return reinterpret_cast<const GlyphPaintGraph
*>(
2487 colrv1
->GetBaseGlyphPaint(aGlyphId
));
2493 bool COLRFonts::PaintGlyphGraph(
2494 hb_blob_t
* aCOLR
, hb_font_t
* aFont
, const GlyphPaintGraph
* aPaintGraph
,
2495 DrawTarget
* aDrawTarget
, layout::TextDrawTarget
* aTextDrawer
,
2496 ScaledFont
* aScaledFont
, DrawOptions aDrawOptions
, const Point
& aPoint
,
2497 const sRGBColor
& aCurrentColor
, const nsTArray
<sRGBColor
>* aColors
,
2498 uint32_t aGlyphId
, float aFontUnitsToPixels
) {
2500 // Currently we always punt to a blob for COLRv1 glyphs.
2501 aTextDrawer
->FoundUnsupportedFeature();
2505 unsigned int coordCount
;
2506 const int* coords
= hb_font_get_var_coords_normalized(aFont
, &coordCount
);
2508 AutoTArray
<uint32_t, 32> visitedOffsets
;
2509 PaintState state
{{nullptr},
2510 aColors
->Elements(),
2515 hb_blob_get_length(aCOLR
),
2518 uint16_t(aColors
->Length()),
2519 uint16_t(coordCount
),
2522 reinterpret_cast<const COLRv1Header
*>(hb_blob_get_data(aCOLR
, nullptr));
2523 AutoRestoreTransform
saveTransform(aDrawTarget
);
2524 aDrawTarget
->ConcatTransform(Matrix::Translation(aPoint
));
2525 return PaintColrGlyph::DoPaint(
2526 state
, reinterpret_cast<const BaseGlyphPaintRecord
*>(aPaintGraph
),
2530 Rect
COLRFonts::GetColorGlyphBounds(hb_blob_t
* aCOLR
, hb_font_t
* aFont
,
2531 uint32_t aGlyphId
, DrawTarget
* aDrawTarget
,
2532 ScaledFont
* aScaledFont
,
2533 float aFontUnitsToPixels
) {
2534 unsigned int coordCount
;
2535 const int* coords
= hb_font_get_var_coords_normalized(aFont
, &coordCount
);
2537 AutoTArray
<uint32_t, 32> visitedOffsets
;
2538 PaintState state
{{nullptr},
2539 nullptr, // palette is not needed
2544 hb_blob_get_length(aCOLR
),
2547 0, // numPaletteEntries
2548 uint16_t(coordCount
),
2551 reinterpret_cast<const COLRv1Header
*>(hb_blob_get_data(aCOLR
, nullptr));
2552 MOZ_ASSERT(uint16_t(state
.mHeader
.v1
->base
.version
) == 1);
2553 // If a clip rect is provided, return this as the glyph bounds.
2554 const auto* clipList
= state
.mHeader
.v1
->clipList();
2556 const auto* clip
= clipList
->GetClip(aGlyphId
);
2558 return clip
->GetRect(state
);
2561 // Otherwise, compute bounds by walking the paint graph.
2562 const auto* base
= state
.mHeader
.v1
->GetBaseGlyphPaint(aGlyphId
);
2564 return DispatchGetBounds(
2565 state
, state
.mHeader
.v1
->baseGlyphListOffset
+ base
->paintOffset
);
2570 uint16_t COLRFonts::GetColrTableVersion(hb_blob_t
* aCOLR
) {
2571 unsigned int blobLength
;
2573 reinterpret_cast<const COLRHeader
*>(hb_blob_get_data(aCOLR
, &blobLength
));
2574 MOZ_ASSERT(colr
, "Cannot get COLR raw data");
2575 MOZ_ASSERT(blobLength
>= sizeof(COLRHeader
), "COLR data too small");
2576 return colr
->version
;
2579 UniquePtr
<nsTArray
<sRGBColor
>> COLRFonts::SetupColorPalette(
2580 hb_face_t
* aFace
, const FontPaletteValueSet
* aPaletteValueSet
,
2581 nsAtom
* aFontPalette
, const nsACString
& aFamilyName
) {
2582 // Find the base color palette to use, if there are multiple available;
2583 // default to first in the font, if nothing matches what is requested.
2584 unsigned int paletteIndex
= 0;
2585 unsigned int count
= hb_ot_color_palette_get_count(aFace
);
2586 MOZ_ASSERT(count
> 0, "No palettes? Font should have been rejected!");
2588 const FontPaletteValueSet::PaletteValues
* fpv
= nullptr;
2589 if (aFontPalette
&& aFontPalette
!= nsGkAtoms::normal
&&
2590 (count
> 1 || aPaletteValueSet
)) {
2591 auto findPalette
= [&](hb_ot_color_palette_flags_t flag
) -> unsigned int {
2592 MOZ_ASSERT(flag
!= HB_OT_COLOR_PALETTE_FLAG_DEFAULT
);
2593 for (unsigned int i
= 0; i
< count
; ++i
) {
2594 if (hb_ot_color_palette_get_flags(aFace
, i
) & flag
) {
2601 if (aFontPalette
== nsGkAtoms::light
) {
2603 findPalette(HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND
);
2604 } else if (aFontPalette
== nsGkAtoms::dark
) {
2606 findPalette(HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND
);
2608 if (aPaletteValueSet
) {
2609 if ((fpv
= aPaletteValueSet
->Lookup(aFontPalette
, aFamilyName
))) {
2610 if (fpv
->mBasePalette
>= 0 && fpv
->mBasePalette
< int32_t(count
)) {
2611 paletteIndex
= fpv
->mBasePalette
;
2612 } else if (fpv
->mBasePalette
==
2613 FontPaletteValueSet::PaletteValues::kLight
) {
2614 paletteIndex
= findPalette(
2615 HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND
);
2616 } else if (fpv
->mBasePalette
==
2617 FontPaletteValueSet::PaletteValues::kDark
) {
2618 paletteIndex
= findPalette(
2619 HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND
);
2626 // Collect the palette colors and convert them to sRGBColor values.
2628 hb_ot_color_palette_get_colors(aFace
, paletteIndex
, 0, nullptr, nullptr);
2629 nsTArray
<hb_color_t
> colors
;
2630 colors
.SetLength(count
);
2631 hb_ot_color_palette_get_colors(aFace
, paletteIndex
, 0, &count
,
2634 auto palette
= MakeUnique
<nsTArray
<sRGBColor
>>();
2635 palette
->SetCapacity(count
);
2636 for (const auto c
: colors
) {
2637 palette
->AppendElement(
2638 sRGBColor(hb_color_get_red(c
) / 255.0, hb_color_get_green(c
) / 255.0,
2639 hb_color_get_blue(c
) / 255.0, hb_color_get_alpha(c
) / 255.0));
2642 // Apply @font-palette-values overrides, if present.
2644 for (const auto overrideColor
: fpv
->mOverrides
) {
2645 if (overrideColor
.mIndex
< palette
->Length()) {
2646 (*palette
)[overrideColor
.mIndex
] = overrideColor
.mColor
;
2654 const FontPaletteValueSet::PaletteValues
* FontPaletteValueSet::Lookup(
2655 nsAtom
* aName
, const nsACString
& aFamily
) const {
2656 nsAutoCString
family(aFamily
);
2657 ToLowerCase(family
);
2658 if (const HashEntry
* entry
=
2659 mValues
.GetEntry(PaletteHashKey(aName
, family
))) {
2660 return &entry
->mValue
;
2665 FontPaletteValueSet::PaletteValues
* FontPaletteValueSet::Insert(
2666 nsAtom
* aName
, const nsACString
& aFamily
) {
2667 nsAutoCString
family(aFamily
);
2668 ToLowerCase(family
);
2669 HashEntry
* entry
= mValues
.PutEntry(PaletteHashKey(aName
, family
));
2670 return &entry
->mValue
;
2673 } // end namespace mozilla::gfx