11import  Anchor  from  './anchor' ; 
22import  { getAnchors ,  getCenterAnchor }  from  './get_anchors' ; 
33import  { shapeText ,  shapeIcon ,  WritingMode ,  fitIconToText ,  isPositionedIcon ,  getPositionedIconSize ,  isFullyStretchableX ,  isFullyStretchableY }  from  './shaping' ; 
4- import  { getGlyphQuads ,  getIconQuads }  from  './quads' ; 
4+ import  { getGlyphQuads ,  getIconQuads ,   getIconQuadsNumber ,   type   SymbolQuad }  from  './quads' ; 
55import  { warnOnce ,  degToRad ,  clamp }  from  '../util/util' ; 
66import  { 
77    allowsVerticalWritingMode , 
@@ -857,11 +857,23 @@ function addFeature(bucket: SymbolBucket,
857857    } 
858858    const  layout  =  bucket . layers [ 0 ] . layout ; 
859859
860+     const  glyphSize  =  ONE_EM ; 
861+     const  fontScale  =  layoutTextSize  *  sizes . textScaleFactor  /  glyphSize ; 
862+ 
860863    const  defaultShaping  =  getDefaultHorizontalShaping ( shapedTextOrientations . horizontal )  ||  shapedTextOrientations . vertical ; 
864+ 
865+     // Store text shaping data for icon-text-fit appearance updates 
866+     if  ( iconTextFit  !==  'none'  &&  bucket . appearanceFeatureData  &&  feature . index  <  bucket . appearanceFeatureData . length )  { 
867+         const  featureData  =  bucket . appearanceFeatureData [ feature . index ] ; 
868+         if  ( featureData )  { 
869+             featureData . textShaping  =  defaultShaping ; 
870+             featureData . iconTextFitPadding  =  layout . get ( 'icon-text-fit-padding' ) . evaluate ( feature ,  { } ,  canonical ) ; 
871+             featureData . fontScale  =  fontScale ; 
872+         } 
873+     } 
861874    const  isGlobe  =  projection . name  ===  'globe' ; 
862875
863-     const  glyphSize  =  ONE_EM , 
864-         textMaxBoxScale  =  bucket . tilePixelRatio  *  textMaxSize  /  glyphSize , 
876+     const  textMaxBoxScale  =  bucket . tilePixelRatio  *  textMaxSize  /  glyphSize , 
865877        iconBoxScale  =  bucket . tilePixelRatio  *  layoutIconSize , 
866878        symbolMinDistance  =  tilePixelRatioForSymbolSpacing ( bucket . overscaling ,  bucket . zoom )  *  layout . get ( 'symbol-spacing' ) , 
867879        textPadding  =  layout . get ( 'text-padding' )  *  bucket . tilePixelRatio , 
@@ -1036,7 +1048,8 @@ function addTextVertices(bucket: SymbolBucket,
10361048        canonical , 
10371049        brightness , 
10381050        false , 
1039-         symbolInstanceIndex ) ; 
1051+         symbolInstanceIndex , 
1052+         glyphQuads . length ) ; 
10401053
10411054    // The placedSymbolArray is used at render time in drawTileSymbols 
10421055    // These indices allow access to the array at collision detection time 
@@ -1229,7 +1242,12 @@ function addSymbol(bucket: SymbolBucket,
12291242        const  iconQuads  =  getIconQuads ( shapedIcon ,  iconRotate ,  isSDFIcon ,  hasIconTextFit ,  sizes . iconScaleFactor ) ; 
12301243        const  verticalIconQuads  =  verticallyShapedIcon  ? getIconQuads ( verticallyShapedIcon ,  iconRotate ,  isSDFIcon ,  hasIconTextFit ,  sizes . iconScaleFactor )  : undefined ; 
12311244        iconBoxIndex  =  evaluateBoxCollisionFeature ( collisionBoxArray ,  collisionFeatureAnchor ,  anchor ,  featureIndex ,  sourceLayerIndex ,  bucketIndex ,  shapedIcon ,  iconPadding ,  iconRotate ,  null ,  iconCollisionBounds ) ; 
1232-         numIconVertices  =  iconQuads . length  *  4 ; 
1245+         // Calculate maximum quads needed across layout icon and all appearance variants 
1246+         // to prevent vertex buffer overflow during appearance updates 
1247+         const  maxQuadCount  =  calculateMaxIconQuadCount ( bucket ,  iconQuads ,  verticalIconQuads , 
1248+             layer . layout ,  feature ,  canonical ,  bucket . iconAtlasPositions , 
1249+             hasIconTextFit ) ; 
1250+         numIconVertices  =  maxQuadCount  *  4 ; 
12331251
12341252        let  iconSizeData  =  null ; 
12351253
@@ -1271,12 +1289,13 @@ function addSymbol(bucket: SymbolBucket,
12711289            canonical , 
12721290            brightness , 
12731291            hasAnySecondaryIcon , 
1274-             bucket . symbolInstances . length ) ; 
1292+             bucket . symbolInstances . length , 
1293+             maxQuadCount ) ; 
12751294
12761295        placedIconSymbolIndex  =  bucket . icon . placedSymbolArray . length  -  1 ; 
12771296
12781297        if  ( verticalIconQuads )  { 
1279-             numVerticalIconVertices  =  verticalIconQuads . length  *  4 ; 
1298+             numVerticalIconVertices  =  maxQuadCount  *  4 ; 
12801299
12811300            bucket . addSymbols ( 
12821301                bucket . icon , 
@@ -1297,7 +1316,8 @@ function addSymbol(bucket: SymbolBucket,
12971316                canonical , 
12981317                brightness , 
12991318                hasAnySecondaryIcon , 
1300-                 bucket . symbolInstances . length ) ; 
1319+                 bucket . symbolInstances . length , 
1320+                 maxQuadCount ) ; 
13011321
13021322            verticalPlacedIconSymbolIndex  =  bucket . icon . placedSymbolArray . length  -  1 ; 
13031323        } 
@@ -1428,3 +1448,58 @@ function anchorIsTooClose(bucket: SymbolBucket, text: string, repeatDistance: nu
14281448    compareText [ text ] . push ( anchor ) ; 
14291449    return  false ; 
14301450} 
1451+ 
1452+ function  calculateMaxIconQuadCount ( 
1453+     bucket : SymbolBucket , 
1454+     iconQuads : Array < SymbolQuad > , 
1455+     verticalIconQuads : Array < SymbolQuad >  |  undefined , 
1456+     layout : PossiblyEvaluated < LayoutProps > , 
1457+     feature : SymbolFeature , 
1458+     canonical : CanonicalTileID , 
1459+     imagePositions : ImagePositionMap , 
1460+     hasIconTextFit : boolean , 
1461+ ) : number  { 
1462+     const  symbolLayer  =  bucket . layers [ 0 ] ; 
1463+     const  appearances  =  symbolLayer . appearances ; 
1464+ 
1465+     // Start with the layout icon quad count 
1466+     let  maxQuadCount  =  iconQuads . length ; 
1467+ 
1468+     // Check vertical icon quads if they exist 
1469+     if  ( verticalIconQuads )  { 
1470+         maxQuadCount  =  Math . max ( maxQuadCount ,  verticalIconQuads . length ) ; 
1471+     } 
1472+ 
1473+     if  ( appearances . length  ===  0 )  { 
1474+         return  maxQuadCount ; 
1475+     } 
1476+ 
1477+     const  [ iconSizeScaleRangeMin ,  iconSizeScaleRangeMax ]  =  layout . get ( 'icon-size-scale-range' ) ; 
1478+     const  iconScaleFactor  =  clamp ( 1 ,  iconSizeScaleRangeMin ,  iconSizeScaleRangeMax ) ; 
1479+ 
1480+     // Check each appearance that has an icon to find maximum quad count needed 
1481+     for  ( const  appearance  of  appearances )  { 
1482+         const  unevaluatedProperties  =  appearance . getUnevaluatedProperties ( ) ; 
1483+         const  iconImageProperty  =  unevaluatedProperties . _values [ 'icon-image' ] . value  !==  undefined ; 
1484+ 
1485+         if  ( iconImageProperty )  { 
1486+             const  appearanceIconImage  =  symbolLayer . getAppearanceValueAndResolveTokens ( appearance ,  'icon-image' ,  feature ,  canonical ,  [ ] ) ; 
1487+             if  ( appearanceIconImage )  { 
1488+                 const  icon  =  bucket . getResolvedImageFromTokens ( appearanceIconImage  as  string ) ; 
1489+                 if  ( icon )  { 
1490+                     // Ideally we shouldn't need to compute the scaled image variant because all 
1491+                     // different sized versions of the same icon have the same number of stretchable 
1492+                     // areas, which is what we need but unfortunately, since imagePositions stores the 
1493+                     // position by the stringified sized icon we need to compute it 
1494+                     const  unevaluatedIconSize  =  unevaluatedProperties . _values [ 'icon-size' ] ; 
1495+                     const  iconSizeData  =  getSizeData ( bucket . zoom ,  unevaluatedIconSize ,  bucket . worldview ) ; 
1496+                     const  imageVariant  =  getScaledImageVariant ( icon ,  iconSizeData ,  unevaluatedIconSize ,  canonical ,  bucket . zoom ,  feature ,  bucket . pixelRatio ,  iconScaleFactor ,  bucket . worldview ) ; 
1497+                     const  imagePosition  =  imagePositions . get ( imageVariant . iconPrimary . toString ( ) ) ; 
1498+                     maxQuadCount  =  Math . max ( maxQuadCount ,  getIconQuadsNumber ( imagePosition ,  hasIconTextFit ) ) ; 
1499+                 } 
1500+             } 
1501+         } 
1502+     } 
1503+ 
1504+     return  maxQuadCount ; 
1505+ } 
0 commit comments