gradiente 2.1.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1179,8 +1179,37 @@ var LinearGradient = class LinearGradient extends GradientBase {
1179
1179
  //#endregion
1180
1180
  //#region src/gradients/RadialGradient.ts
1181
1181
  var RadialGradient = class RadialGradient extends GradientBase {
1182
+ static DEFAULT_CONFIG = {
1183
+ isRepeating: false,
1184
+ stops: [{
1185
+ type: "color-stop",
1186
+ value: "red",
1187
+ position: 0
1188
+ }, {
1189
+ type: "color-stop",
1190
+ value: "blue",
1191
+ position: 1
1192
+ }],
1193
+ config: {
1194
+ shape: "ellipse",
1195
+ size: {
1196
+ kind: "extent",
1197
+ value: "farthest-corner"
1198
+ },
1199
+ position: {
1200
+ kind: "keywords",
1201
+ x: "center",
1202
+ y: "center"
1203
+ }
1204
+ }
1205
+ };
1182
1206
  type = "radial-gradient";
1183
- constructor(config) {
1207
+ constructor(input) {
1208
+ const config = {
1209
+ ...RadialGradient.DEFAULT_CONFIG,
1210
+ ...input
1211
+ };
1212
+ if (config.config.interpolation) config.config.interpolation = RadialGradient._normalizeConfigInterpolation(config.config.interpolation);
1184
1213
  super(config);
1185
1214
  }
1186
1215
  static fromString(input) {
@@ -1201,31 +1230,47 @@ var RadialGradient = class RadialGradient extends GradientBase {
1201
1230
  return new RadialGradient(this.toJSON());
1202
1231
  }
1203
1232
  toString() {
1204
- return `${this.isRepeating ? `repeating-${this.type}` : this.type}(${[this._serializeRadialConfig(this.config), ...this._serializeStopsCompact()].filter(Boolean).join(", ")})`;
1233
+ return `${this.isRepeating ? `repeating-${this.type}` : this.type}(${[this._parseConfigToString(this.config), ...this._serializeStopsCompact()].filter(Boolean).join(", ")})`;
1205
1234
  }
1206
1235
  _validateConfig(config) {
1207
1236
  if (config.shape !== "circle" && config.shape !== "ellipse") throw new Error("Invalid shape");
1208
1237
  if (!config.position) throw new Error("Position is required");
1209
1238
  if (!config.size) throw new Error("Size is required");
1210
1239
  }
1211
- _serializeRadialConfig(config) {
1240
+ _parseConfigToString(config) {
1241
+ const configParts = [];
1242
+ const radialConfigString = this._parseRadialConfigToString(config);
1243
+ if (radialConfigString.length > 0) configParts.push(radialConfigString);
1244
+ if (config.interpolation !== void 0) configParts.push(this._parseInterpolationToString(config.interpolation));
1245
+ return configParts.join(" ");
1246
+ }
1247
+ _parseRadialConfigToString(config) {
1212
1248
  const parts = [];
1213
- parts.push(config.shape);
1214
- if (config.size.kind === "extent") parts.push(config.size.value);
1215
- else {
1216
- const x = this._formatLengthPercentage(config.size.x);
1217
- const y = config.size.y ? ` ${this._formatLengthPercentage(config.size.y)}` : "";
1218
- parts.push(`${x}${y}`);
1219
- }
1220
- parts.push(`at ${this._serializePosition(config.position)}`);
1221
- if (config.interpolation) if (config.interpolation.kind === "rectangular") parts.push(`in ${config.interpolation.space}`);
1222
- else {
1223
- let str = `in ${config.interpolation.space}`;
1224
- if (config.interpolation.hueMethod) str += ` ${config.interpolation.hueMethod} hue`;
1225
- parts.push(str);
1226
- }
1249
+ if (!this._isDefaultRadialShape(config.shape)) parts.push(config.shape);
1250
+ if (!this._isDefaultRadialSize(config.size)) parts.push(this._parseRadialSizeToString(config.size));
1251
+ if (!this._isDefaultRadialPosition(config.position)) parts.push(`at ${this._serializePosition(config.position)}`);
1227
1252
  return parts.join(" ");
1228
1253
  }
1254
+ _parseRadialSizeToString(size) {
1255
+ if (size.kind === "extent") return size.value;
1256
+ const x = this._formatLengthPercentage(size.x);
1257
+ if (size.y === void 0) return x;
1258
+ return `${x} ${this._formatLengthPercentage(size.y)}`;
1259
+ }
1260
+ _parseInterpolationToString(interpolation) {
1261
+ const { colorSpace, hue } = interpolation;
1262
+ if (hue === void 0) return `in ${colorSpace}`;
1263
+ return `in ${colorSpace} ${hue} hue`;
1264
+ }
1265
+ _isDefaultRadialShape(shape) {
1266
+ return shape === "ellipse";
1267
+ }
1268
+ _isDefaultRadialSize(size) {
1269
+ return size.kind === "extent" && size.value === "farthest-corner";
1270
+ }
1271
+ _isDefaultRadialPosition(position) {
1272
+ return position.kind === "keywords" && position.x === "center" && position.y === "center";
1273
+ }
1229
1274
  _serializePosition(position) {
1230
1275
  if (position.kind === "keywords") return `${position.x} ${position.y}`;
1231
1276
  const x = this._formatLengthPercentage(position.x);
@@ -1247,6 +1292,11 @@ var RadialGradient = class RadialGradient extends GradientBase {
1247
1292
  x: "center",
1248
1293
  y: "center"
1249
1294
  };
1295
+ let interpolation;
1296
+ const isLengthPercentage = (value) => {
1297
+ if (value === void 0) return false;
1298
+ return value.endsWith("%") || /^-?\d*\.?\d+[a-zA-Z]+$/.test(value);
1299
+ };
1250
1300
  for (const input of inputs) {
1251
1301
  if (input.type !== "config") continue;
1252
1302
  const tokens = splitTopLevelByWhitespace(input.value);
@@ -1263,20 +1313,88 @@ var RadialGradient = class RadialGradient extends GradientBase {
1263
1313
  };
1264
1314
  continue;
1265
1315
  }
1316
+ if (isLengthPercentage(t)) {
1317
+ const nextToken = tokens[i + 1];
1318
+ if (shape === "ellipse" && isLengthPercentage(nextToken)) {
1319
+ size = {
1320
+ kind: "explicit",
1321
+ x: this._parseLengthPercentage(t),
1322
+ y: this._parseLengthPercentage(nextToken)
1323
+ };
1324
+ i += 1;
1325
+ continue;
1326
+ }
1327
+ size = {
1328
+ kind: "explicit",
1329
+ x: this._parseLengthPercentage(t)
1330
+ };
1331
+ continue;
1332
+ }
1266
1333
  if (t === "at") {
1267
1334
  const xToken = tokens[i + 1];
1268
1335
  const yToken = tokens[i + 2];
1269
- if ((xToken === "left" || xToken === "center" || xToken === "right") && (yToken === "top" || yToken === "center" || yToken === "bottom")) position = {
1270
- kind: "keywords",
1271
- x: xToken,
1272
- y: yToken
1273
- };
1274
- else position = {
1275
- kind: "values",
1276
- x: this._parseLengthPercentage(xToken),
1277
- y: this._parseLengthPercentage(yToken)
1278
- };
1279
- i += 2;
1336
+ if (xToken === "center" && (yToken === void 0 || yToken === "in")) {
1337
+ position = {
1338
+ kind: "keywords",
1339
+ x: "center",
1340
+ y: "center"
1341
+ };
1342
+ i += 1;
1343
+ continue;
1344
+ }
1345
+ if ((xToken === "left" || xToken === "right") && (yToken === void 0 || yToken === "in")) {
1346
+ position = {
1347
+ kind: "keywords",
1348
+ x: xToken,
1349
+ y: "center"
1350
+ };
1351
+ i += 1;
1352
+ continue;
1353
+ }
1354
+ if ((xToken === "top" || xToken === "bottom") && (yToken === void 0 || yToken === "in")) {
1355
+ position = {
1356
+ kind: "keywords",
1357
+ x: "center",
1358
+ y: xToken
1359
+ };
1360
+ i += 1;
1361
+ continue;
1362
+ }
1363
+ if ((xToken === "left" || xToken === "center" || xToken === "right") && (yToken === "top" || yToken === "center" || yToken === "bottom")) {
1364
+ position = {
1365
+ kind: "keywords",
1366
+ x: xToken,
1367
+ y: yToken
1368
+ };
1369
+ i += 2;
1370
+ continue;
1371
+ }
1372
+ if (isLengthPercentage(xToken) && isLengthPercentage(yToken)) {
1373
+ position = {
1374
+ kind: "values",
1375
+ x: this._parseLengthPercentage(xToken),
1376
+ y: this._parseLengthPercentage(yToken)
1377
+ };
1378
+ i += 2;
1379
+ continue;
1380
+ }
1381
+ throw new Error(`Invalid radial-gradient position: ${xToken ?? ""} ${yToken ?? ""}`);
1382
+ }
1383
+ if (t === "in") {
1384
+ const colorSpace = tokens[i + 1];
1385
+ const maybeHue = tokens[i + 2];
1386
+ const maybeHueKeyword = tokens[i + 3];
1387
+ if (!colorSpace) throw new Error("Invalid radial-gradient interpolation: missing color space");
1388
+ if (maybeHue !== void 0 && maybeHueKeyword === "hue") {
1389
+ interpolation = this._normalizeConfigInterpolation({
1390
+ colorSpace,
1391
+ hue: maybeHue
1392
+ });
1393
+ i += 3;
1394
+ continue;
1395
+ }
1396
+ interpolation = this._normalizeConfigInterpolation({ colorSpace });
1397
+ i += 1;
1280
1398
  continue;
1281
1399
  }
1282
1400
  }
@@ -1284,7 +1402,8 @@ var RadialGradient = class RadialGradient extends GradientBase {
1284
1402
  return {
1285
1403
  shape,
1286
1404
  size,
1287
- position
1405
+ position,
1406
+ interpolation
1288
1407
  };
1289
1408
  }
1290
1409
  static _parseLengthPercentage(input) {
@@ -1300,6 +1419,15 @@ var RadialGradient = class RadialGradient extends GradientBase {
1300
1419
  unit: match[2]
1301
1420
  };
1302
1421
  }
1422
+ static _normalizeConfigInterpolation(value) {
1423
+ const { colorSpace, hue } = value;
1424
+ if (hue === void 0) return { colorSpace };
1425
+ if (!isGradientPolarColorSpace(colorSpace)) return { colorSpace };
1426
+ return {
1427
+ colorSpace,
1428
+ hue
1429
+ };
1430
+ }
1303
1431
  };
1304
1432
  //#endregion
1305
1433
  //#region src/gradients/ConicGradient.ts
@@ -1493,7 +1621,7 @@ function sampleColorAtPosition(stops, position) {
1493
1621
  function sampleRepeatingColorAtPosition(stops, position, firstPosition, period) {
1494
1622
  return sampleColorAtPosition(stops, firstPosition + positiveModulo(position - firstPosition, period));
1495
1623
  }
1496
- function expandRepeatingStops(stops) {
1624
+ function expandRepeatingStopsTo(stops, from, to) {
1497
1625
  const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
1498
1626
  if (colorStops.length < 2) return colorStops;
1499
1627
  const firstPosition = colorStops[0].position;
@@ -1503,18 +1631,18 @@ function expandRepeatingStops(stops) {
1503
1631
  let order = 0;
1504
1632
  result.push({
1505
1633
  type: "color-stop",
1506
- value: sampleRepeatingColorAtPosition(colorStops, 0, firstPosition, period),
1507
- position: 0,
1634
+ value: sampleRepeatingColorAtPosition(colorStops, from, firstPosition, period),
1635
+ position: from,
1508
1636
  _order: order
1509
1637
  });
1510
1638
  order += 1;
1511
- const startRepeat = Math.floor((0 - firstPosition) / period) - 1;
1512
- const endRepeat = Math.ceil((1 - firstPosition) / period) + 1;
1639
+ const startRepeat = Math.floor((from - firstPosition) / period) - 1;
1640
+ const endRepeat = Math.ceil((to - firstPosition) / period) + 1;
1513
1641
  for (let repeatIndex = startRepeat; repeatIndex <= endRepeat; repeatIndex += 1) {
1514
1642
  const offset = repeatIndex * period;
1515
1643
  for (const stop of colorStops) {
1516
1644
  const position = stop.position + offset;
1517
- if (position <= 0 || position >= 1) continue;
1645
+ if (position <= from || position >= to) continue;
1518
1646
  result.push({
1519
1647
  ...stop,
1520
1648
  position,
@@ -1525,8 +1653,8 @@ function expandRepeatingStops(stops) {
1525
1653
  }
1526
1654
  result.push({
1527
1655
  type: "color-stop",
1528
- value: sampleRepeatingColorAtPosition(colorStops, 1, firstPosition, period),
1529
- position: 1,
1656
+ value: sampleRepeatingColorAtPosition(colorStops, to, firstPosition, period),
1657
+ position: to,
1530
1658
  _order: order
1531
1659
  });
1532
1660
  return result.sort((a, b) => {
@@ -1534,6 +1662,33 @@ function expandRepeatingStops(stops) {
1534
1662
  return a.position - b.position;
1535
1663
  }).map(({ _order, ...stop }) => stop);
1536
1664
  }
1665
+ function expandRepeatingStops(stops) {
1666
+ return expandRepeatingStopsTo(stops, 0, 1);
1667
+ }
1668
+ function getMaxVisibleRadialT(center, radii, width, height) {
1669
+ return Math.max(...[
1670
+ {
1671
+ x: 0,
1672
+ y: 0
1673
+ },
1674
+ {
1675
+ x: width,
1676
+ y: 0
1677
+ },
1678
+ {
1679
+ x: 0,
1680
+ y: height
1681
+ },
1682
+ {
1683
+ x: width,
1684
+ y: height
1685
+ }
1686
+ ].map((corner) => {
1687
+ const dx = (corner.x - center.x) / Math.max(radii.x, 1e-4);
1688
+ const dy = (corner.y - center.y) / Math.max(radii.y, 1e-4);
1689
+ return Math.sqrt(dx * dx + dy * dy);
1690
+ }));
1691
+ }
1537
1692
  //#endregion
1538
1693
  //#region src/gradient-transformer/modules/helpers/resolve-renderable-linear-gradient-stops.ts
1539
1694
  function getHueFixup(hue) {
@@ -1583,13 +1738,13 @@ function getColorStopsWithPositions(stops) {
1583
1738
  });
1584
1739
  }
1585
1740
  function formatColorForCanvas(input) {
1586
- const color = toRgb$4(input);
1741
+ const color = toRgb$5(input);
1587
1742
  if (!color) throw new Error("Failed to convert interpolated color to rgb.");
1588
1743
  return formatRgb(color);
1589
1744
  }
1590
1745
  const DEFAULT_SAMPLE_COUNT = 64;
1591
- const toRgb$4 = converter("rgb");
1592
- function resolveRenderableLinearGradientStops(gradient, sampleCount = DEFAULT_SAMPLE_COUNT) {
1746
+ const toRgb$5 = converter("rgb");
1747
+ function resolveRenderableGradientStops(gradient, sampleCount = DEFAULT_SAMPLE_COUNT) {
1593
1748
  const colorStops = getColorStopsWithPositions(gradient.stops);
1594
1749
  const interpolation = gradient.config.interpolation;
1595
1750
  if (colorStops.length < 2) return colorStops;
@@ -1619,9 +1774,9 @@ function resolveRenderableLinearGradientStops(gradient, sampleCount = DEFAULT_SA
1619
1774
  }
1620
1775
  //#endregion
1621
1776
  //#region src/gradient-transformer/modules/canvas/ModuleTransformerLinearGradientToCanvas.ts
1622
- const toRgb$3 = converter("rgb");
1777
+ const toRgb$4 = converter("rgb");
1623
1778
  function toCanvasColor$1(input) {
1624
- const color = toRgb$3(input);
1779
+ const color = toRgb$4(input);
1625
1780
  if (!color) throw new Error(`Failed to convert color: ${input}`);
1626
1781
  return formatRgb(color);
1627
1782
  }
@@ -1638,7 +1793,7 @@ function getStopRange$2(stops) {
1638
1793
  stops: colorStops
1639
1794
  };
1640
1795
  }
1641
- function normalizeStops$2(stops, min, max) {
1796
+ function normalizeStops$3(stops, min, max) {
1642
1797
  const range = max - min || 1;
1643
1798
  return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
1644
1799
  ...stop,
@@ -1661,7 +1816,7 @@ var ModuleTransformerLinearGradientToCanvas = class {
1661
1816
  let startY = centerY - dirY * lineLength / 2;
1662
1817
  let endX = centerX + dirX * lineLength / 2;
1663
1818
  let endY = centerY + dirY * lineLength / 2;
1664
- const { min, max, stops } = getStopRange$2(resolveRenderableLinearGradientStops(gradient));
1819
+ const { min, max, stops } = getStopRange$2(resolveRenderableGradientStops(gradient));
1665
1820
  let normalizedStops = stops;
1666
1821
  if (min < 0 || max > 1) {
1667
1822
  const vx = endX - startX;
@@ -1672,7 +1827,7 @@ var ModuleTransformerLinearGradientToCanvas = class {
1672
1827
  startY = baseStartY + vy * min;
1673
1828
  endX = baseStartX + vx * max;
1674
1829
  endY = baseStartY + vy * max;
1675
- normalizedStops = normalizeStops$2(stops, min, max);
1830
+ normalizedStops = normalizeStops$3(stops, min, max);
1676
1831
  }
1677
1832
  const canvasGradient = ctx.createLinearGradient(startX, startY, endX, endY);
1678
1833
  for (const stop of normalizedStops) canvasGradient.addColorStop(stop.position, toCanvasColor$1(stop.value));
@@ -1684,9 +1839,10 @@ var ModuleTransformerLinearGradientToCanvas = class {
1684
1839
  };
1685
1840
  //#endregion
1686
1841
  //#region src/gradient-transformer/modules/canvas/ModuleTransformerRadialGradientToCanvas.ts
1687
- const toRgb$2 = converter("rgb");
1842
+ const toRgb$3 = converter("rgb");
1843
+ const RADIAL_GRADIENT_SAMPLE_COUNT = 128;
1688
1844
  function toCanvasColor(input) {
1689
- const color = toRgb$2(input);
1845
+ const color = toRgb$3(input);
1690
1846
  if (!color) throw new Error(`Failed to convert color: ${input}`);
1691
1847
  return formatRgb(color);
1692
1848
  }
@@ -1703,47 +1859,200 @@ function getStopRange$1(stops) {
1703
1859
  stops: colorStops
1704
1860
  };
1705
1861
  }
1706
- function normalizeStops$1(stops, min, max) {
1862
+ function normalizeStops$2(stops, min, max) {
1707
1863
  const range = max - min || 1;
1708
1864
  return stops.map((stop) => ({
1709
1865
  ...stop,
1710
1866
  position: (stop.position - min) / range
1711
1867
  }));
1712
1868
  }
1869
+ function getDistanceToSide$1(center, width, height, side) {
1870
+ if (side === "left") return center.x;
1871
+ if (side === "right") return width - center.x;
1872
+ if (side === "top") return center.y;
1873
+ return height - center.y;
1874
+ }
1875
+ function getDistanceToCorner$1(center, corner) {
1876
+ const dx = corner.x - center.x;
1877
+ const dy = corner.y - center.y;
1878
+ return Math.sqrt(dx * dx + dy * dy);
1879
+ }
1880
+ function getCornerDeltas$1(center, width, height) {
1881
+ return [
1882
+ {
1883
+ dx: -center.x,
1884
+ dy: -center.y
1885
+ },
1886
+ {
1887
+ dx: width - center.x,
1888
+ dy: -center.y
1889
+ },
1890
+ {
1891
+ dx: -center.x,
1892
+ dy: height - center.y
1893
+ },
1894
+ {
1895
+ dx: width - center.x,
1896
+ dy: height - center.y
1897
+ }
1898
+ ];
1899
+ }
1900
+ function scaleEllipseRadiiToCorner$1(radiusX, radiusY, dx, dy) {
1901
+ const safeRadiusX = Math.max(radiusX, 1e-4);
1902
+ const safeRadiusY = Math.max(radiusY, 1e-4);
1903
+ const scale = Math.sqrt(dx * dx / (safeRadiusX * safeRadiusX) + dy * dy / (safeRadiusY * safeRadiusY));
1904
+ return {
1905
+ x: safeRadiusX * scale,
1906
+ y: safeRadiusY * scale
1907
+ };
1908
+ }
1713
1909
  var ModuleTransformerRadialGradientToCanvas = class {
1714
1910
  target = "canvas-2d";
1715
1911
  gradientType = "radial-gradient";
1716
1912
  to(input) {
1717
1913
  const gradient = input;
1718
1914
  return { draw: (ctx, width, height) => {
1719
- const pos = gradient.config.position;
1720
- let x = width / 2;
1721
- let y = height / 2;
1722
- if (pos.kind === "values") {
1723
- x = this._resolve(pos.x, width);
1724
- y = this._resolve(pos.y, height);
1725
- }
1726
- const dx = Math.max(x, width - x);
1727
- const dy = Math.max(y, height - y);
1728
- const radius = Math.sqrt(dx * dx + dy * dy);
1729
- const { min, max, stops } = getStopRange$1(gradient.stops);
1730
- let innerRadius = 0;
1731
- let outerRadius = radius;
1915
+ const center = this._resolveCenter(gradient.config.position, width, height);
1916
+ const radii = this._resolveRadialRadii(gradient.config.size, gradient.config.shape, center, width, height);
1917
+ const maxVisibleT = getMaxVisibleRadialT(center, radii, width, height);
1918
+ const baseStops = resolveRenderableGradientStops(gradient, RADIAL_GRADIENT_SAMPLE_COUNT);
1919
+ const { min, max, stops } = getStopRange$1(gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, maxVisibleT) : baseStops);
1732
1920
  let normalizedStops = stops;
1733
- if (min >= 0 && (min < 0 || max > 1)) {
1734
- innerRadius = radius * min;
1735
- outerRadius = radius * max;
1736
- normalizedStops = normalizeStops$1(stops, min, max);
1737
- } else if (max > 1) {
1738
- outerRadius = radius * max;
1739
- normalizedStops = normalizeStops$1(stops, min, max);
1921
+ let innerFactor = 0;
1922
+ let outerFactor = gradient.isRepeating ? maxVisibleT : 1;
1923
+ if (gradient.isRepeating) normalizedStops = normalizeStops$2(stops, 0, maxVisibleT);
1924
+ else if (min < 0 || max > 1) {
1925
+ normalizedStops = normalizeStops$2(stops, min, max);
1926
+ innerFactor = min;
1927
+ outerFactor = max;
1740
1928
  }
1741
- const g = ctx.createRadialGradient(x, y, innerRadius, x, y, outerRadius);
1929
+ if (gradient.config.shape === "circle") {
1930
+ const baseRadius = radii.x;
1931
+ const innerRadius = Math.max(0, baseRadius * innerFactor);
1932
+ const outerRadius = Math.max(innerRadius + 1e-4, baseRadius * outerFactor);
1933
+ const g = ctx.createRadialGradient(center.x, center.y, innerRadius, center.x, center.y, outerRadius);
1934
+ for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor(stop.value));
1935
+ ctx.fillStyle = g;
1936
+ ctx.fillRect(0, 0, width, height);
1937
+ return;
1938
+ }
1939
+ const outerRadius = Math.max(radii.x, radii.y);
1940
+ const scaleX = radii.x / outerRadius;
1941
+ const scaleY = radii.y / outerRadius;
1942
+ const innerRadius = Math.max(0, outerRadius * innerFactor);
1943
+ const scaledOuterRadius = Math.max(innerRadius + 1e-4, outerRadius * outerFactor);
1944
+ ctx.save();
1945
+ ctx.translate(center.x, center.y);
1946
+ ctx.scale(scaleX, scaleY);
1947
+ const g = ctx.createRadialGradient(0, 0, innerRadius, 0, 0, scaledOuterRadius);
1742
1948
  for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor(stop.value));
1743
1949
  ctx.fillStyle = g;
1744
- ctx.fillRect(0, 0, width, height);
1950
+ const drawRadius = scaledOuterRadius + 2;
1951
+ ctx.fillRect(-drawRadius / scaleX * 2, -drawRadius / scaleY * 2, drawRadius / scaleX * 4, drawRadius / scaleY * 4);
1952
+ ctx.restore();
1745
1953
  } };
1746
1954
  }
1955
+ _resolveCenter(position, width, height) {
1956
+ if (position.kind === "keywords") return {
1957
+ x: this._resolveKeywordX(position.x, width),
1958
+ y: this._resolveKeywordY(position.y, height)
1959
+ };
1960
+ return {
1961
+ x: this._resolve(position.x, width),
1962
+ y: this._resolve(position.y, height)
1963
+ };
1964
+ }
1965
+ _resolveKeywordX(value, width) {
1966
+ if (value === "left") return 0;
1967
+ if (value === "center") return width / 2;
1968
+ return width;
1969
+ }
1970
+ _resolveKeywordY(value, height) {
1971
+ if (value === "top") return 0;
1972
+ if (value === "center") return height / 2;
1973
+ return height;
1974
+ }
1975
+ _resolveRadialRadii(size, shape, center, width, height) {
1976
+ if (size.kind === "explicit") {
1977
+ const radiusX = this._resolve(size.x, width);
1978
+ const radiusY = size.y ? this._resolve(size.y, height) : radiusX;
1979
+ return {
1980
+ x: Math.max(radiusX, 1e-4),
1981
+ y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
1982
+ };
1983
+ }
1984
+ const left = getDistanceToSide$1(center, width, height, "left");
1985
+ const right = getDistanceToSide$1(center, width, height, "right");
1986
+ const top = getDistanceToSide$1(center, width, height, "top");
1987
+ const bottom = getDistanceToSide$1(center, width, height, "bottom");
1988
+ if (shape === "circle") {
1989
+ const cornerDistances = [
1990
+ {
1991
+ x: 0,
1992
+ y: 0
1993
+ },
1994
+ {
1995
+ x: width,
1996
+ y: 0
1997
+ },
1998
+ {
1999
+ x: 0,
2000
+ y: height
2001
+ },
2002
+ {
2003
+ x: width,
2004
+ y: height
2005
+ }
2006
+ ].map((corner) => getDistanceToCorner$1(center, corner));
2007
+ if (size.value === "closest-side") {
2008
+ const radius = Math.min(left, right, top, bottom);
2009
+ return {
2010
+ x: radius,
2011
+ y: radius
2012
+ };
2013
+ }
2014
+ if (size.value === "farthest-side") {
2015
+ const radius = Math.max(left, right, top, bottom);
2016
+ return {
2017
+ x: radius,
2018
+ y: radius
2019
+ };
2020
+ }
2021
+ if (size.value === "closest-corner") {
2022
+ const radius = Math.min(...cornerDistances);
2023
+ return {
2024
+ x: radius,
2025
+ y: radius
2026
+ };
2027
+ }
2028
+ const radius = Math.max(...cornerDistances);
2029
+ return {
2030
+ x: radius,
2031
+ y: radius
2032
+ };
2033
+ }
2034
+ const closestSideRadiusX = Math.min(left, right);
2035
+ const closestSideRadiusY = Math.min(top, bottom);
2036
+ const farthestSideRadiusX = Math.max(left, right);
2037
+ const farthestSideRadiusY = Math.max(top, bottom);
2038
+ if (size.value === "closest-side") return {
2039
+ x: Math.max(closestSideRadiusX, 1e-4),
2040
+ y: Math.max(closestSideRadiusY, 1e-4)
2041
+ };
2042
+ if (size.value === "farthest-side") return {
2043
+ x: Math.max(farthestSideRadiusX, 1e-4),
2044
+ y: Math.max(farthestSideRadiusY, 1e-4)
2045
+ };
2046
+ const corners = getCornerDeltas$1(center, width, height);
2047
+ if (size.value === "closest-corner") return corners.map((corner) => scaleEllipseRadiiToCorner$1(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => {
2048
+ const closestArea = closest.x * closest.y;
2049
+ return current.x * current.y < closestArea ? current : closest;
2050
+ });
2051
+ return corners.map((corner) => scaleEllipseRadiiToCorner$1(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => {
2052
+ const farthestArea = farthest.x * farthest.y;
2053
+ return current.x * current.y > farthestArea ? current : farthest;
2054
+ });
2055
+ }
1747
2056
  _resolve(value, size) {
1748
2057
  if (value.kind === "percent") return value.value / 100 * size;
1749
2058
  if (value.unit === "px") return value.value;
@@ -1752,7 +2061,7 @@ var ModuleTransformerRadialGradientToCanvas = class {
1752
2061
  };
1753
2062
  //#endregion
1754
2063
  //#region src/gradient-transformer/modules/canvas/ModuleTransformerConicGradientToCanvas.ts
1755
- const toRgb$1 = converter("rgb");
2064
+ const toRgb$2 = converter("rgb");
1756
2065
  var ModuleTransformerConicGradientToCanvas = class {
1757
2066
  target = "canvas-2d";
1758
2067
  gradientType = "conic-gradient";
@@ -1851,7 +2160,7 @@ var ModuleTransformerConicGradientToCanvas = class {
1851
2160
  };
1852
2161
  }
1853
2162
  _parseColor(input) {
1854
- const color = toRgb$1(input);
2163
+ const color = toRgb$2(input);
1855
2164
  if (!color) throw new Error(`Failed to convert color: ${input}`);
1856
2165
  return {
1857
2166
  r: Math.round((color.r ?? 0) * 255),
@@ -1866,10 +2175,10 @@ var ModuleTransformerConicGradientToCanvas = class {
1866
2175
  };
1867
2176
  //#endregion
1868
2177
  //#region src/gradient-transformer/modules/webgl/ModuleTransformerLinearGradientToWebgl.ts
1869
- const toRgb = converter("rgb");
1870
- const MAX_STOPS = 128;
1871
- function toWebGLColor(input) {
1872
- const color = toRgb(input);
2178
+ const toRgb$1 = converter("rgb");
2179
+ const MAX_STOPS$1 = 128;
2180
+ function toWebGLColor$1(input) {
2181
+ const color = toRgb$1(input);
1873
2182
  if (!color) throw new Error(`Failed to convert color: ${input}`);
1874
2183
  return [
1875
2184
  color.r ?? 0,
@@ -1891,14 +2200,14 @@ function getStopRange(stops) {
1891
2200
  stops: colorStops
1892
2201
  };
1893
2202
  }
1894
- function normalizeStops(stops, min, max) {
2203
+ function normalizeStops$1(stops, min, max) {
1895
2204
  const range = max - min || 1;
1896
2205
  return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
1897
2206
  ...stop,
1898
2207
  position: (stop.position - min) / range
1899
2208
  }));
1900
2209
  }
1901
- function createShader(gl, type, source) {
2210
+ function createShader$1(gl, type, source) {
1902
2211
  const shader = gl.createShader(type);
1903
2212
  if (!shader) throw new Error("Failed to create WebGL shader.");
1904
2213
  gl.shaderSource(shader, source);
@@ -1910,9 +2219,9 @@ function createShader(gl, type, source) {
1910
2219
  }
1911
2220
  return shader;
1912
2221
  }
1913
- function createProgram(gl, vertexSource, fragmentSource) {
1914
- const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
1915
- const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
2222
+ function createProgram$1(gl, vertexSource, fragmentSource) {
2223
+ const vertexShader = createShader$1(gl, gl.VERTEX_SHADER, vertexSource);
2224
+ const fragmentShader = createShader$1(gl, gl.FRAGMENT_SHADER, fragmentSource);
1916
2225
  const program = gl.createProgram();
1917
2226
  if (!program) throw new Error("Failed to create WebGL program.");
1918
2227
  gl.attachShader(program, vertexShader);
@@ -1925,15 +2234,15 @@ function createProgram(gl, vertexSource, fragmentSource) {
1925
2234
  }
1926
2235
  return program;
1927
2236
  }
1928
- function getColorStopCount(stops) {
2237
+ function getColorStopCount$1(stops) {
1929
2238
  return stops.filter((stop) => stop.type === "color-stop").length;
1930
2239
  }
1931
- function getWebGLSampleCount(gradient, maxStops) {
1932
- const colorStopCount = getColorStopCount(gradient.stops);
2240
+ function getWebGLSampleCount$1(gradient, maxStops) {
2241
+ const colorStopCount = getColorStopCount$1(gradient.stops);
1933
2242
  const segmentCount = Math.max(1, colorStopCount - 1);
1934
2243
  return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
1935
2244
  }
1936
- function getColorAtPosition(stops, position) {
2245
+ function getColorAtPosition$1(stops, position) {
1937
2246
  const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
1938
2247
  if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
1939
2248
  if (position <= colorStops[0].position) return colorStops[0].value;
@@ -1950,7 +2259,7 @@ function getColorAtPosition(stops, position) {
1950
2259
  }
1951
2260
  return lastStop.value;
1952
2261
  }
1953
- function fitStopsToWebGLLimit(stops, maxStops) {
2262
+ function fitStopsToWebGLLimit$1(stops, maxStops) {
1954
2263
  const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
1955
2264
  if (colorStops.length <= maxStops) return colorStops;
1956
2265
  const sampledStops = [];
@@ -1958,7 +2267,7 @@ function fitStopsToWebGLLimit(stops, maxStops) {
1958
2267
  const position = index / (maxStops - 1);
1959
2268
  sampledStops.push({
1960
2269
  type: "color-stop",
1961
- value: getColorAtPosition(colorStops, position),
2270
+ value: getColorAtPosition$1(colorStops, position),
1962
2271
  position
1963
2272
  });
1964
2273
  }
@@ -1975,7 +2284,7 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
1975
2284
  canvas.width = width;
1976
2285
  canvas.height = height;
1977
2286
  gl.viewport(0, 0, width, height);
1978
- const program = createProgram(gl, `
2287
+ const program = createProgram$1(gl, `
1979
2288
  attribute vec2 a_position;
1980
2289
  varying vec2 v_uv;
1981
2290
 
@@ -1991,13 +2300,13 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
1991
2300
  uniform vec2 u_start;
1992
2301
  uniform vec2 u_end;
1993
2302
  uniform int u_stopCount;
1994
- uniform float u_positions[${MAX_STOPS}];
1995
- uniform vec4 u_colors[${MAX_STOPS}];
2303
+ uniform float u_positions[${MAX_STOPS$1}];
2304
+ uniform vec4 u_colors[${MAX_STOPS$1}];
1996
2305
 
1997
2306
  vec4 getGradientColor(float t) {
1998
2307
  vec4 result = u_colors[0];
1999
2308
 
2000
- for (int i = 0; i < ${MAX_STOPS - 1}; i++) {
2309
+ for (int i = 0; i < ${MAX_STOPS$1 - 1}; i++) {
2001
2310
  if (i >= u_stopCount - 1) {
2002
2311
  break;
2003
2312
  }
@@ -2062,7 +2371,7 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
2062
2371
  let startY = centerY - dirY * lineLength / 2;
2063
2372
  let endX = centerX + dirX * lineLength / 2;
2064
2373
  let endY = centerY + dirY * lineLength / 2;
2065
- const { min, max, stops } = getStopRange(resolveRenderableLinearGradientStops(gradient, getWebGLSampleCount(gradient, MAX_STOPS)));
2374
+ const { min, max, stops } = getStopRange(resolveRenderableGradientStops(gradient, getWebGLSampleCount$1(gradient, MAX_STOPS$1)));
2066
2375
  let normalizedStops = stops;
2067
2376
  if (min < 0 || max > 1) {
2068
2377
  const vx = endX - startX;
@@ -2073,13 +2382,373 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
2073
2382
  startY = baseStartY + vy * min;
2074
2383
  endX = baseStartX + vx * max;
2075
2384
  endY = baseStartY + vy * max;
2076
- normalizedStops = normalizeStops(stops, min, max);
2385
+ normalizedStops = normalizeStops$1(stops, min, max);
2077
2386
  }
2078
2387
  const startU = startX / width;
2079
2388
  const startV = 1 - startY / height;
2080
2389
  const endU = endX / width;
2081
2390
  const endV = 1 - endY / height;
2082
- const limitedStops = fitStopsToWebGLLimit(normalizedStops, MAX_STOPS);
2391
+ const limitedStops = fitStopsToWebGLLimit$1(normalizedStops, MAX_STOPS$1);
2392
+ const positions = new Float32Array(MAX_STOPS$1);
2393
+ const colors = new Float32Array(MAX_STOPS$1 * 4);
2394
+ limitedStops.forEach((stop, index) => {
2395
+ const color = toWebGLColor$1(stop.value);
2396
+ positions[index] = stop.position;
2397
+ colors[index * 4 + 0] = color[0];
2398
+ colors[index * 4 + 1] = color[1];
2399
+ colors[index * 4 + 2] = color[2];
2400
+ colors[index * 4 + 3] = color[3];
2401
+ });
2402
+ gl.uniform2f(gl.getUniformLocation(program, "u_start"), startU, startV);
2403
+ gl.uniform2f(gl.getUniformLocation(program, "u_end"), endU, endV);
2404
+ gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
2405
+ gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
2406
+ gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
2407
+ gl.clearColor(0, 0, 0, 0);
2408
+ gl.clear(gl.COLOR_BUFFER_BIT);
2409
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
2410
+ } };
2411
+ }
2412
+ };
2413
+ //#endregion
2414
+ //#region src/gradient-transformer/modules/webgl/ModuleTransformerRadialGradientToWebgl.ts
2415
+ const toRgb = converter("rgb");
2416
+ const MAX_STOPS = 128;
2417
+ const MAX_REPEATING_RADIAL_T = 16;
2418
+ function toWebGLColor(input) {
2419
+ const color = toRgb(input);
2420
+ if (!color) throw new Error(`Failed to convert color: ${input}`);
2421
+ return [
2422
+ color.r ?? 0,
2423
+ color.g ?? 0,
2424
+ color.b ?? 0,
2425
+ color.alpha ?? 1
2426
+ ];
2427
+ }
2428
+ function createShader(gl, type, source) {
2429
+ const shader = gl.createShader(type);
2430
+ if (!shader) throw new Error("Failed to create WebGL shader.");
2431
+ gl.shaderSource(shader, source);
2432
+ gl.compileShader(shader);
2433
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
2434
+ const error = gl.getShaderInfoLog(shader);
2435
+ gl.deleteShader(shader);
2436
+ throw new Error(`WebGL shader compile error: ${error}`);
2437
+ }
2438
+ return shader;
2439
+ }
2440
+ function createProgram(gl, vertexSource, fragmentSource) {
2441
+ const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
2442
+ const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
2443
+ const program = gl.createProgram();
2444
+ if (!program) throw new Error("Failed to create WebGL program.");
2445
+ gl.attachShader(program, vertexShader);
2446
+ gl.attachShader(program, fragmentShader);
2447
+ gl.linkProgram(program);
2448
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
2449
+ const error = gl.getProgramInfoLog(program);
2450
+ gl.deleteProgram(program);
2451
+ throw new Error(`WebGL program link error: ${error}`);
2452
+ }
2453
+ return program;
2454
+ }
2455
+ function getColorStopCount(stops) {
2456
+ return stops.filter((stop) => stop.type === "color-stop").length;
2457
+ }
2458
+ function getWebGLSampleCount(gradient, maxStops) {
2459
+ const colorStopCount = getColorStopCount(gradient.stops);
2460
+ const segmentCount = Math.max(1, colorStopCount - 1);
2461
+ return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
2462
+ }
2463
+ function getColorAtPosition(stops, position) {
2464
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
2465
+ if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
2466
+ if (position <= colorStops[0].position) return colorStops[0].value;
2467
+ const lastStop = colorStops[colorStops.length - 1];
2468
+ if (position >= lastStop.position) return lastStop.value;
2469
+ for (let index = 0; index < colorStops.length - 1; index += 1) {
2470
+ const current = colorStops[index];
2471
+ const next = colorStops[index + 1];
2472
+ if (position >= current.position && position <= next.position) {
2473
+ const range = next.position - current.position || 1;
2474
+ const localT = (position - current.position) / range;
2475
+ return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
2476
+ }
2477
+ }
2478
+ return lastStop.value;
2479
+ }
2480
+ function fitStopsToWebGLLimit(stops, maxStops) {
2481
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
2482
+ if (colorStops.length <= maxStops) return colorStops;
2483
+ const sampledStops = [];
2484
+ for (let index = 0; index < maxStops; index += 1) {
2485
+ const position = index / (maxStops - 1);
2486
+ sampledStops.push({
2487
+ type: "color-stop",
2488
+ value: getColorAtPosition(colorStops, position),
2489
+ position
2490
+ });
2491
+ }
2492
+ return sampledStops;
2493
+ }
2494
+ function parseLengthPercentage(value, reference) {
2495
+ if (value.kind === "percent") return value.value / 100 * reference;
2496
+ if (value.kind === "length") {
2497
+ if (value.unit === "px") return value.value;
2498
+ throw new Error(`Unsupported gradient length unit for WebGL radial gradient: ${value.unit}`);
2499
+ }
2500
+ return value;
2501
+ }
2502
+ function resolveKeywordPositionX(x, width) {
2503
+ if (x === "left") return 0;
2504
+ if (x === "center") return width / 2;
2505
+ if (x === "right") return width;
2506
+ return width / 2;
2507
+ }
2508
+ function resolveKeywordPositionY(y, height) {
2509
+ if (y === "top") return 0;
2510
+ if (y === "center") return height / 2;
2511
+ if (y === "bottom") return height;
2512
+ return height / 2;
2513
+ }
2514
+ function resolveRadialCenter(position, width, height) {
2515
+ if (position.kind === "keywords") return {
2516
+ x: resolveKeywordPositionX(position.x, width),
2517
+ y: resolveKeywordPositionY(position.y, height)
2518
+ };
2519
+ if (position.kind === "values") return {
2520
+ x: parseLengthPercentage(position.x, width),
2521
+ y: parseLengthPercentage(position.y, height)
2522
+ };
2523
+ return {
2524
+ x: width / 2,
2525
+ y: height / 2
2526
+ };
2527
+ }
2528
+ function getDistanceToSide(center, width, height, side) {
2529
+ if (side === "left") return center.x;
2530
+ if (side === "right") return width - center.x;
2531
+ if (side === "top") return center.y;
2532
+ return height - center.y;
2533
+ }
2534
+ function getDistanceToCorner(center, corner) {
2535
+ const dx = corner.x - center.x;
2536
+ const dy = corner.y - center.y;
2537
+ return Math.sqrt(dx * dx + dy * dy);
2538
+ }
2539
+ function normalizeStops(stops, min, max) {
2540
+ const range = max - min || 1;
2541
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
2542
+ ...stop,
2543
+ position: (stop.position - min) / range
2544
+ }));
2545
+ }
2546
+ function getCornerDeltas(center, width, height) {
2547
+ return [
2548
+ {
2549
+ dx: -center.x,
2550
+ dy: -center.y
2551
+ },
2552
+ {
2553
+ dx: width - center.x,
2554
+ dy: -center.y
2555
+ },
2556
+ {
2557
+ dx: -center.x,
2558
+ dy: height - center.y
2559
+ },
2560
+ {
2561
+ dx: width - center.x,
2562
+ dy: height - center.y
2563
+ }
2564
+ ];
2565
+ }
2566
+ function scaleEllipseRadiiToCorner(radiusX, radiusY, dx, dy) {
2567
+ const safeRadiusX = Math.max(radiusX, 1e-4);
2568
+ const safeRadiusY = Math.max(radiusY, 1e-4);
2569
+ const scale = Math.sqrt(dx * dx / (safeRadiusX * safeRadiusX) + dy * dy / (safeRadiusY * safeRadiusY));
2570
+ return {
2571
+ x: safeRadiusX * scale,
2572
+ y: safeRadiusY * scale
2573
+ };
2574
+ }
2575
+ function resolveRadialRadii(size, shape, center, width, height) {
2576
+ if (size.kind === "explicit") {
2577
+ const radiusX = parseLengthPercentage(size.x, width);
2578
+ const radiusY = size.y ? parseLengthPercentage(size.y, height) : radiusX;
2579
+ return {
2580
+ x: Math.max(radiusX, 1e-4),
2581
+ y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
2582
+ };
2583
+ }
2584
+ const left = getDistanceToSide(center, width, height, "left");
2585
+ const right = getDistanceToSide(center, width, height, "right");
2586
+ const top = getDistanceToSide(center, width, height, "top");
2587
+ const bottom = getDistanceToSide(center, width, height, "bottom");
2588
+ if (shape === "circle") {
2589
+ const cornerDistances = [
2590
+ {
2591
+ x: 0,
2592
+ y: 0
2593
+ },
2594
+ {
2595
+ x: width,
2596
+ y: 0
2597
+ },
2598
+ {
2599
+ x: 0,
2600
+ y: height
2601
+ },
2602
+ {
2603
+ x: width,
2604
+ y: height
2605
+ }
2606
+ ].map((corner) => getDistanceToCorner(center, corner));
2607
+ if (size.value === "closest-side") {
2608
+ const radius = Math.max(Math.min(left, right, top, bottom), 1e-4);
2609
+ return {
2610
+ x: radius,
2611
+ y: radius
2612
+ };
2613
+ }
2614
+ if (size.value === "farthest-side") {
2615
+ const radius = Math.max(Math.max(left, right, top, bottom), 1e-4);
2616
+ return {
2617
+ x: radius,
2618
+ y: radius
2619
+ };
2620
+ }
2621
+ if (size.value === "closest-corner") {
2622
+ const radius = Math.max(Math.min(...cornerDistances), 1e-4);
2623
+ return {
2624
+ x: radius,
2625
+ y: radius
2626
+ };
2627
+ }
2628
+ const radius = Math.max(Math.max(...cornerDistances), 1e-4);
2629
+ return {
2630
+ x: radius,
2631
+ y: radius
2632
+ };
2633
+ }
2634
+ const closestSideRadiusX = Math.min(left, right);
2635
+ const closestSideRadiusY = Math.min(top, bottom);
2636
+ const farthestSideRadiusX = Math.max(left, right);
2637
+ const farthestSideRadiusY = Math.max(top, bottom);
2638
+ if (size.value === "closest-side") return {
2639
+ x: Math.max(closestSideRadiusX, 1e-4),
2640
+ y: Math.max(closestSideRadiusY, 1e-4)
2641
+ };
2642
+ if (size.value === "farthest-side") return {
2643
+ x: Math.max(farthestSideRadiusX, 1e-4),
2644
+ y: Math.max(farthestSideRadiusY, 1e-4)
2645
+ };
2646
+ const corners = getCornerDeltas(center, width, height);
2647
+ if (size.value === "closest-corner") return corners.map((corner) => scaleEllipseRadiiToCorner(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => {
2648
+ const closestArea = closest.x * closest.y;
2649
+ return current.x * current.y < closestArea ? current : closest;
2650
+ });
2651
+ return corners.map((corner) => scaleEllipseRadiiToCorner(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => {
2652
+ const farthestArea = farthest.x * farthest.y;
2653
+ return current.x * current.y > farthestArea ? current : farthest;
2654
+ });
2655
+ }
2656
+ var ModuleTransformerRadialGradientToCanvasWebGL = class {
2657
+ target = "canvas-webgl";
2658
+ gradientType = "radial-gradient";
2659
+ to(input) {
2660
+ const gradient = input;
2661
+ return { draw: (canvas, width, height) => {
2662
+ const gl = canvas.getContext("webgl");
2663
+ if (!gl) throw new Error("WebGL is not supported.");
2664
+ canvas.width = width;
2665
+ canvas.height = height;
2666
+ gl.viewport(0, 0, width, height);
2667
+ const program = createProgram(gl, `
2668
+ attribute vec2 a_position;
2669
+ varying vec2 v_uv;
2670
+
2671
+ void main() {
2672
+ v_uv = a_position * 0.5 + 0.5;
2673
+ gl_Position = vec4(a_position, 0.0, 1.0);
2674
+ }
2675
+ `, `
2676
+ precision mediump float;
2677
+
2678
+ varying vec2 v_uv;
2679
+
2680
+ uniform vec2 u_center;
2681
+ uniform vec2 u_radius;
2682
+ uniform int u_stopCount;
2683
+ uniform float u_positions[${MAX_STOPS}];
2684
+ uniform vec4 u_colors[${MAX_STOPS}];
2685
+ uniform float u_tMax;
2686
+
2687
+ vec4 getGradientColor(float t) {
2688
+ vec4 result = u_colors[0];
2689
+
2690
+ for (int i = 0; i < ${MAX_STOPS - 1}; i++) {
2691
+ if (i >= u_stopCount - 1) {
2692
+ break;
2693
+ }
2694
+
2695
+ float currentPosition = u_positions[i];
2696
+ float nextPosition = u_positions[i + 1];
2697
+
2698
+ if (t <= currentPosition) {
2699
+ return u_colors[i];
2700
+ }
2701
+
2702
+ if (t >= currentPosition && t <= nextPosition) {
2703
+ float localT = (t - currentPosition) / max(nextPosition - currentPosition, 0.00001);
2704
+ return mix(u_colors[i], u_colors[i + 1], localT);
2705
+ }
2706
+
2707
+ result = u_colors[i + 1];
2708
+ }
2709
+
2710
+ return result;
2711
+ }
2712
+
2713
+ void main() {
2714
+ vec2 delta = v_uv - u_center;
2715
+ vec2 normalized = delta / max(u_radius, vec2(0.00001));
2716
+ float t = length(normalized);
2717
+
2718
+ t = clamp(t, 0.0, u_tMax);
2719
+ t = t / max(u_tMax, 0.00001);
2720
+
2721
+ gl_FragColor = getGradientColor(t);
2722
+ }
2723
+ `);
2724
+ gl.useProgram(program);
2725
+ const positionBuffer = gl.createBuffer();
2726
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
2727
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
2728
+ -1,
2729
+ -1,
2730
+ 1,
2731
+ -1,
2732
+ -1,
2733
+ 1,
2734
+ -1,
2735
+ 1,
2736
+ 1,
2737
+ -1,
2738
+ 1,
2739
+ 1
2740
+ ]), gl.STATIC_DRAW);
2741
+ const positionLocation = gl.getAttribLocation(program, "a_position");
2742
+ gl.enableVertexAttribArray(positionLocation);
2743
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
2744
+ const center = resolveRadialCenter(gradient.config.position, width, height);
2745
+ const radius = resolveRadialRadii(gradient.config.size, gradient.config.shape, center, width, height);
2746
+ const maxVisibleT = getMaxVisibleRadialT(center, radius, width, height);
2747
+ const baseStops = resolveRenderableGradientStops(gradient, getWebGLSampleCount(gradient, MAX_STOPS));
2748
+ const repeatMaxT = Math.min(maxVisibleT, MAX_REPEATING_RADIAL_T);
2749
+ const maxT = gradient.isRepeating ? repeatMaxT : 1;
2750
+ const renderStops = gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, repeatMaxT) : baseStops;
2751
+ const limitedStops = fitStopsToWebGLLimit(gradient.isRepeating ? normalizeStops(renderStops, 0, repeatMaxT) : renderStops, MAX_STOPS);
2083
2752
  const positions = new Float32Array(MAX_STOPS);
2084
2753
  const colors = new Float32Array(MAX_STOPS * 4);
2085
2754
  limitedStops.forEach((stop, index) => {
@@ -2090,8 +2759,9 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
2090
2759
  colors[index * 4 + 2] = color[2];
2091
2760
  colors[index * 4 + 3] = color[3];
2092
2761
  });
2093
- gl.uniform2f(gl.getUniformLocation(program, "u_start"), startU, startV);
2094
- gl.uniform2f(gl.getUniformLocation(program, "u_end"), endU, endV);
2762
+ gl.uniform2f(gl.getUniformLocation(program, "u_center"), center.x / width, 1 - center.y / height);
2763
+ gl.uniform2f(gl.getUniformLocation(program, "u_radius"), radius.x / width, radius.y / height);
2764
+ gl.uniform1f(gl.getUniformLocation(program, "u_tMax"), maxT);
2095
2765
  gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
2096
2766
  gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
2097
2767
  gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
@@ -2139,6 +2809,7 @@ var GradientTransformer = class {
2139
2809
  this.add(new ModuleTransformerRadialGradientToCanvas());
2140
2810
  this.add(new ModuleTransformerConicGradientToCanvas());
2141
2811
  this.add(new ModuleTransformerLinearGradientToCanvasWebGL());
2812
+ this.add(new ModuleTransformerRadialGradientToCanvasWebGL());
2142
2813
  }
2143
2814
  static _getKey(target, gradientType) {
2144
2815
  return `${target}:${gradientType}`;
@@ -2200,4 +2871,4 @@ function transformFrom(target, gradientType, input) {
2200
2871
  return GradientTransformer.from(target, gradientType, input);
2201
2872
  }
2202
2873
  //#endregion
2203
- export { ConicGradient, GRADIENT_COLOR_SPACE, GRADIENT_HUE_INTERPOLATIONS, GRADIENT_POLAR_COLOR_SPACES, GradientBase, GradientFactory, GradientTransformer, LinearGradient, ModuleTransformerConicGradientToCanvas, ModuleTransformerConicGradientToCss, ModuleTransformerLinearGradientToCanvas, ModuleTransformerLinearGradientToCanvasWebGL, ModuleTransformerLinearGradientToCss, ModuleTransformerRadialGradientToCanvas, ModuleTransformerRadialGradientToCss, PatternTokenKind, RadialGradient, angleValueFromString, ceilTo, clamp, degToRad, floorTo, format, fromPercent, gradToRad, isAngle, isAngleUnit, isColorHint, isColorStop, isConfig, isGradient, isGradientColorSpace, isGradientHueInterpolation, isGradientPolarColorSpace, isPatternSyntaxValid, isPatternValid, isValid, matchExpression, normalizeAngleDeg, normalizeAngleRad, parse, parseStringToAbi, radToDeg, roundTo, splitTopLevelByWhitespace, toPercent, tokenizePattern, transformFrom, transformTo, truncTo, turnToRad, validate, validatePattern, validatePatternSemantic, validatePatternStructure, validatePatternSyntax };
2874
+ export { ConicGradient, GRADIENT_COLOR_SPACE, GRADIENT_HUE_INTERPOLATIONS, GRADIENT_POLAR_COLOR_SPACES, GradientBase, GradientFactory, GradientTransformer, LinearGradient, ModuleTransformerConicGradientToCanvas, ModuleTransformerConicGradientToCss, ModuleTransformerLinearGradientToCanvas, ModuleTransformerLinearGradientToCanvasWebGL, ModuleTransformerLinearGradientToCss, ModuleTransformerRadialGradientToCanvas, ModuleTransformerRadialGradientToCanvasWebGL, ModuleTransformerRadialGradientToCss, PatternTokenKind, RadialGradient, angleValueFromString, ceilTo, clamp, degToRad, floorTo, format, fromPercent, gradToRad, isAngle, isAngleUnit, isColorHint, isColorStop, isConfig, isGradient, isGradientColorSpace, isGradientHueInterpolation, isGradientPolarColorSpace, isPatternSyntaxValid, isPatternValid, isValid, matchExpression, normalizeAngleDeg, normalizeAngleRad, parse, parseStringToAbi, radToDeg, roundTo, splitTopLevelByWhitespace, toPercent, tokenizePattern, transformFrom, transformTo, truncTo, turnToRad, validate, validatePattern, validatePatternSemantic, validatePatternStructure, validatePatternSyntax };