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/gradiente.global.iife.js +58 -2
- package/dist/index.d.ts +31 -9
- package/dist/index.js +768 -97
- package/package.json +1 -1
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(
|
|
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.
|
|
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
|
-
|
|
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
|
|
1215
|
-
|
|
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 (
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
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
|
|
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,
|
|
1507
|
-
position:
|
|
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((
|
|
1512
|
-
const endRepeat = Math.ceil((
|
|
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 <=
|
|
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,
|
|
1529
|
-
position:
|
|
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$
|
|
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$
|
|
1592
|
-
function
|
|
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$
|
|
1777
|
+
const toRgb$4 = converter("rgb");
|
|
1623
1778
|
function toCanvasColor$1(input) {
|
|
1624
|
-
const color = toRgb$
|
|
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$
|
|
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(
|
|
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$
|
|
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$
|
|
1842
|
+
const toRgb$3 = converter("rgb");
|
|
1843
|
+
const RADIAL_GRADIENT_SAMPLE_COUNT = 128;
|
|
1688
1844
|
function toCanvasColor(input) {
|
|
1689
|
-
const color = toRgb$
|
|
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$
|
|
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
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
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
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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$
|
|
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$
|
|
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(
|
|
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, "
|
|
2094
|
-
gl.uniform2f(gl.getUniformLocation(program, "
|
|
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 };
|