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