gradiente 2.1.2 → 2.3.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 +116 -2
- package/dist/index.d.ts +43 -10
- package/dist/index.js +1087 -133
- 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
|
|
@@ -1332,18 +1460,12 @@ var ConicGradient = class ConicGradient extends GradientBase {
|
|
|
1332
1460
|
_validateConfig(config) {}
|
|
1333
1461
|
_serializeConfig() {
|
|
1334
1462
|
const parts = [];
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
if (this.config.interpolation) {
|
|
1339
|
-
const i = this.config.interpolation;
|
|
1340
|
-
if (i.kind === "rectangular") parts.push(`in ${i.space}`);
|
|
1341
|
-
else {
|
|
1342
|
-
let str = `in ${i.space}`;
|
|
1343
|
-
if (i.hueMethod) str += ` ${i.hueMethod} hue`;
|
|
1344
|
-
parts.push(str);
|
|
1345
|
-
}
|
|
1463
|
+
if (!this._isDefaultFrom(this.config.from)) {
|
|
1464
|
+
const angle = this.config.from;
|
|
1465
|
+
parts.push(`from ${angle.value}${angle.unit}`);
|
|
1346
1466
|
}
|
|
1467
|
+
if (!this._isDefaultPosition(this.config.position)) parts.push(`at ${this._serializePosition(this.config.position)}`);
|
|
1468
|
+
if (this.config.interpolation !== void 0) parts.push(this._serializeInterpolation(this.config.interpolation));
|
|
1347
1469
|
return parts.join(" ");
|
|
1348
1470
|
}
|
|
1349
1471
|
_serializePosition(position) {
|
|
@@ -1356,6 +1478,17 @@ var ConicGradient = class ConicGradient extends GradientBase {
|
|
|
1356
1478
|
if (value.kind === "percent") return `${value.value}%`;
|
|
1357
1479
|
return `${value.value}${value.unit}`;
|
|
1358
1480
|
}
|
|
1481
|
+
_serializeInterpolation(interpolation) {
|
|
1482
|
+
const { colorSpace, hue } = interpolation;
|
|
1483
|
+
if (hue === void 0) return `in ${colorSpace}`;
|
|
1484
|
+
return `in ${colorSpace} ${hue} hue`;
|
|
1485
|
+
}
|
|
1486
|
+
_isDefaultFrom(from) {
|
|
1487
|
+
return from.value === 0 && from.unit === "deg";
|
|
1488
|
+
}
|
|
1489
|
+
_isDefaultPosition(position) {
|
|
1490
|
+
return position.kind === "keywords" && position.x === "center" && position.y === "center";
|
|
1491
|
+
}
|
|
1359
1492
|
static _parseConfig(input) {
|
|
1360
1493
|
const config = {
|
|
1361
1494
|
from: {
|
|
@@ -1371,46 +1504,86 @@ var ConicGradient = class ConicGradient extends GradientBase {
|
|
|
1371
1504
|
};
|
|
1372
1505
|
if (!input) return config;
|
|
1373
1506
|
const tokens = splitTopLevelByWhitespace(input);
|
|
1507
|
+
const isLengthPercentage = (value) => {
|
|
1508
|
+
if (value === void 0) return false;
|
|
1509
|
+
return value.endsWith("%") || /^-?\d*\.?\d+[a-zA-Z]+$/.test(value);
|
|
1510
|
+
};
|
|
1374
1511
|
for (let i = 0; i < tokens.length; i++) {
|
|
1375
1512
|
const token = tokens[i];
|
|
1376
1513
|
if (token === "from") {
|
|
1377
|
-
|
|
1514
|
+
const angleToken = tokens[i + 1];
|
|
1515
|
+
if (angleToken === void 0) throw new Error("Invalid conic-gradient config: missing angle after from");
|
|
1516
|
+
config.from = this._parseAngle(angleToken);
|
|
1378
1517
|
i += 1;
|
|
1379
1518
|
continue;
|
|
1380
1519
|
}
|
|
1381
1520
|
if (token === "at") {
|
|
1382
1521
|
const xToken = tokens[i + 1];
|
|
1383
1522
|
const yToken = tokens[i + 2];
|
|
1384
|
-
if (
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1523
|
+
if (xToken === void 0) throw new Error("Invalid conic-gradient config: missing position after at");
|
|
1524
|
+
if (xToken === "center" && (yToken === void 0 || yToken === "in")) {
|
|
1525
|
+
config.position = {
|
|
1526
|
+
kind: "keywords",
|
|
1527
|
+
x: "center",
|
|
1528
|
+
y: "center"
|
|
1529
|
+
};
|
|
1530
|
+
i += 1;
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
if ((xToken === "left" || xToken === "right") && (yToken === void 0 || yToken === "in")) {
|
|
1534
|
+
config.position = {
|
|
1535
|
+
kind: "keywords",
|
|
1536
|
+
x: xToken,
|
|
1537
|
+
y: "center"
|
|
1538
|
+
};
|
|
1539
|
+
i += 1;
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
if ((xToken === "top" || xToken === "bottom") && (yToken === void 0 || yToken === "in")) {
|
|
1543
|
+
config.position = {
|
|
1544
|
+
kind: "keywords",
|
|
1545
|
+
x: "center",
|
|
1546
|
+
y: xToken
|
|
1547
|
+
};
|
|
1548
|
+
i += 1;
|
|
1549
|
+
continue;
|
|
1550
|
+
}
|
|
1551
|
+
if ((xToken === "left" || xToken === "center" || xToken === "right") && (yToken === "top" || yToken === "center" || yToken === "bottom")) {
|
|
1552
|
+
config.position = {
|
|
1553
|
+
kind: "keywords",
|
|
1554
|
+
x: xToken,
|
|
1555
|
+
y: yToken
|
|
1556
|
+
};
|
|
1557
|
+
i += 2;
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
if (isLengthPercentage(xToken) && isLengthPercentage(yToken)) {
|
|
1561
|
+
config.position = {
|
|
1562
|
+
kind: "values",
|
|
1563
|
+
x: this._parseLengthPercentage(xToken),
|
|
1564
|
+
y: this._parseLengthPercentage(yToken)
|
|
1565
|
+
};
|
|
1566
|
+
i += 2;
|
|
1567
|
+
continue;
|
|
1568
|
+
}
|
|
1569
|
+
throw new Error(`Invalid conic-gradient position: ${xToken} ${yToken ?? ""}`);
|
|
1396
1570
|
}
|
|
1397
1571
|
if (token === "in") {
|
|
1398
|
-
const
|
|
1399
|
-
const
|
|
1400
|
-
|
|
1572
|
+
const colorSpace = tokens[i + 1];
|
|
1573
|
+
const maybeHue = tokens[i + 2];
|
|
1574
|
+
const maybeHueKeyword = tokens[i + 3];
|
|
1575
|
+
if (colorSpace === void 0) throw new Error("Invalid conic-gradient interpolation: missing color space");
|
|
1576
|
+
if (maybeHueKeyword === "hue" && (maybeHue === "shorter" || maybeHue === "longer" || maybeHue === "increasing" || maybeHue === "decreasing")) {
|
|
1401
1577
|
config.interpolation = {
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
hueMethod
|
|
1578
|
+
colorSpace,
|
|
1579
|
+
hue: maybeHue
|
|
1405
1580
|
};
|
|
1406
1581
|
i += 3;
|
|
1407
1582
|
continue;
|
|
1408
1583
|
}
|
|
1409
|
-
config.interpolation = {
|
|
1410
|
-
kind: "rectangular",
|
|
1411
|
-
space
|
|
1412
|
-
};
|
|
1584
|
+
config.interpolation = { colorSpace };
|
|
1413
1585
|
i += 1;
|
|
1586
|
+
continue;
|
|
1414
1587
|
}
|
|
1415
1588
|
}
|
|
1416
1589
|
return config;
|
|
@@ -1493,7 +1666,7 @@ function sampleColorAtPosition(stops, position) {
|
|
|
1493
1666
|
function sampleRepeatingColorAtPosition(stops, position, firstPosition, period) {
|
|
1494
1667
|
return sampleColorAtPosition(stops, firstPosition + positiveModulo(position - firstPosition, period));
|
|
1495
1668
|
}
|
|
1496
|
-
function
|
|
1669
|
+
function expandRepeatingStopsTo(stops, from, to) {
|
|
1497
1670
|
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
1498
1671
|
if (colorStops.length < 2) return colorStops;
|
|
1499
1672
|
const firstPosition = colorStops[0].position;
|
|
@@ -1503,18 +1676,18 @@ function expandRepeatingStops(stops) {
|
|
|
1503
1676
|
let order = 0;
|
|
1504
1677
|
result.push({
|
|
1505
1678
|
type: "color-stop",
|
|
1506
|
-
value: sampleRepeatingColorAtPosition(colorStops,
|
|
1507
|
-
position:
|
|
1679
|
+
value: sampleRepeatingColorAtPosition(colorStops, from, firstPosition, period),
|
|
1680
|
+
position: from,
|
|
1508
1681
|
_order: order
|
|
1509
1682
|
});
|
|
1510
1683
|
order += 1;
|
|
1511
|
-
const startRepeat = Math.floor((
|
|
1512
|
-
const endRepeat = Math.ceil((
|
|
1684
|
+
const startRepeat = Math.floor((from - firstPosition) / period) - 1;
|
|
1685
|
+
const endRepeat = Math.ceil((to - firstPosition) / period) + 1;
|
|
1513
1686
|
for (let repeatIndex = startRepeat; repeatIndex <= endRepeat; repeatIndex += 1) {
|
|
1514
1687
|
const offset = repeatIndex * period;
|
|
1515
1688
|
for (const stop of colorStops) {
|
|
1516
1689
|
const position = stop.position + offset;
|
|
1517
|
-
if (position <=
|
|
1690
|
+
if (position <= from || position >= to) continue;
|
|
1518
1691
|
result.push({
|
|
1519
1692
|
...stop,
|
|
1520
1693
|
position,
|
|
@@ -1525,8 +1698,8 @@ function expandRepeatingStops(stops) {
|
|
|
1525
1698
|
}
|
|
1526
1699
|
result.push({
|
|
1527
1700
|
type: "color-stop",
|
|
1528
|
-
value: sampleRepeatingColorAtPosition(colorStops,
|
|
1529
|
-
position:
|
|
1701
|
+
value: sampleRepeatingColorAtPosition(colorStops, to, firstPosition, period),
|
|
1702
|
+
position: to,
|
|
1530
1703
|
_order: order
|
|
1531
1704
|
});
|
|
1532
1705
|
return result.sort((a, b) => {
|
|
@@ -1534,6 +1707,33 @@ function expandRepeatingStops(stops) {
|
|
|
1534
1707
|
return a.position - b.position;
|
|
1535
1708
|
}).map(({ _order, ...stop }) => stop);
|
|
1536
1709
|
}
|
|
1710
|
+
function expandRepeatingStops(stops) {
|
|
1711
|
+
return expandRepeatingStopsTo(stops, 0, 1);
|
|
1712
|
+
}
|
|
1713
|
+
function getMaxVisibleRadialT(center, radii, width, height) {
|
|
1714
|
+
return Math.max(...[
|
|
1715
|
+
{
|
|
1716
|
+
x: 0,
|
|
1717
|
+
y: 0
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
x: width,
|
|
1721
|
+
y: 0
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
x: 0,
|
|
1725
|
+
y: height
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
x: width,
|
|
1729
|
+
y: height
|
|
1730
|
+
}
|
|
1731
|
+
].map((corner) => {
|
|
1732
|
+
const dx = (corner.x - center.x) / Math.max(radii.x, 1e-4);
|
|
1733
|
+
const dy = (corner.y - center.y) / Math.max(radii.y, 1e-4);
|
|
1734
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
1735
|
+
}));
|
|
1736
|
+
}
|
|
1537
1737
|
//#endregion
|
|
1538
1738
|
//#region src/gradient-transformer/modules/helpers/resolve-renderable-linear-gradient-stops.ts
|
|
1539
1739
|
function getHueFixup(hue) {
|
|
@@ -1583,13 +1783,13 @@ function getColorStopsWithPositions(stops) {
|
|
|
1583
1783
|
});
|
|
1584
1784
|
}
|
|
1585
1785
|
function formatColorForCanvas(input) {
|
|
1586
|
-
const color = toRgb$
|
|
1786
|
+
const color = toRgb$6(input);
|
|
1587
1787
|
if (!color) throw new Error("Failed to convert interpolated color to rgb.");
|
|
1588
1788
|
return formatRgb(color);
|
|
1589
1789
|
}
|
|
1590
1790
|
const DEFAULT_SAMPLE_COUNT = 64;
|
|
1591
|
-
const toRgb$
|
|
1592
|
-
function
|
|
1791
|
+
const toRgb$6 = converter("rgb");
|
|
1792
|
+
function resolveRenderableGradientStops(gradient, sampleCount = DEFAULT_SAMPLE_COUNT) {
|
|
1593
1793
|
const colorStops = getColorStopsWithPositions(gradient.stops);
|
|
1594
1794
|
const interpolation = gradient.config.interpolation;
|
|
1595
1795
|
if (colorStops.length < 2) return colorStops;
|
|
@@ -1619,9 +1819,9 @@ function resolveRenderableLinearGradientStops(gradient, sampleCount = DEFAULT_SA
|
|
|
1619
1819
|
}
|
|
1620
1820
|
//#endregion
|
|
1621
1821
|
//#region src/gradient-transformer/modules/canvas/ModuleTransformerLinearGradientToCanvas.ts
|
|
1622
|
-
const toRgb$
|
|
1822
|
+
const toRgb$5 = converter("rgb");
|
|
1623
1823
|
function toCanvasColor$1(input) {
|
|
1624
|
-
const color = toRgb$
|
|
1824
|
+
const color = toRgb$5(input);
|
|
1625
1825
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
1626
1826
|
return formatRgb(color);
|
|
1627
1827
|
}
|
|
@@ -1638,7 +1838,7 @@ function getStopRange$2(stops) {
|
|
|
1638
1838
|
stops: colorStops
|
|
1639
1839
|
};
|
|
1640
1840
|
}
|
|
1641
|
-
function normalizeStops$
|
|
1841
|
+
function normalizeStops$3(stops, min, max) {
|
|
1642
1842
|
const range = max - min || 1;
|
|
1643
1843
|
return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
|
|
1644
1844
|
...stop,
|
|
@@ -1661,7 +1861,7 @@ var ModuleTransformerLinearGradientToCanvas = class {
|
|
|
1661
1861
|
let startY = centerY - dirY * lineLength / 2;
|
|
1662
1862
|
let endX = centerX + dirX * lineLength / 2;
|
|
1663
1863
|
let endY = centerY + dirY * lineLength / 2;
|
|
1664
|
-
const { min, max, stops } = getStopRange$2(
|
|
1864
|
+
const { min, max, stops } = getStopRange$2(resolveRenderableGradientStops(gradient));
|
|
1665
1865
|
let normalizedStops = stops;
|
|
1666
1866
|
if (min < 0 || max > 1) {
|
|
1667
1867
|
const vx = endX - startX;
|
|
@@ -1672,7 +1872,7 @@ var ModuleTransformerLinearGradientToCanvas = class {
|
|
|
1672
1872
|
startY = baseStartY + vy * min;
|
|
1673
1873
|
endX = baseStartX + vx * max;
|
|
1674
1874
|
endY = baseStartY + vy * max;
|
|
1675
|
-
normalizedStops = normalizeStops$
|
|
1875
|
+
normalizedStops = normalizeStops$3(stops, min, max);
|
|
1676
1876
|
}
|
|
1677
1877
|
const canvasGradient = ctx.createLinearGradient(startX, startY, endX, endY);
|
|
1678
1878
|
for (const stop of normalizedStops) canvasGradient.addColorStop(stop.position, toCanvasColor$1(stop.value));
|
|
@@ -1684,9 +1884,10 @@ var ModuleTransformerLinearGradientToCanvas = class {
|
|
|
1684
1884
|
};
|
|
1685
1885
|
//#endregion
|
|
1686
1886
|
//#region src/gradient-transformer/modules/canvas/ModuleTransformerRadialGradientToCanvas.ts
|
|
1687
|
-
const toRgb$
|
|
1887
|
+
const toRgb$4 = converter("rgb");
|
|
1888
|
+
const RADIAL_GRADIENT_SAMPLE_COUNT = 128;
|
|
1688
1889
|
function toCanvasColor(input) {
|
|
1689
|
-
const color = toRgb$
|
|
1890
|
+
const color = toRgb$4(input);
|
|
1690
1891
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
1691
1892
|
return formatRgb(color);
|
|
1692
1893
|
}
|
|
@@ -1703,47 +1904,200 @@ function getStopRange$1(stops) {
|
|
|
1703
1904
|
stops: colorStops
|
|
1704
1905
|
};
|
|
1705
1906
|
}
|
|
1706
|
-
function normalizeStops$
|
|
1907
|
+
function normalizeStops$2(stops, min, max) {
|
|
1707
1908
|
const range = max - min || 1;
|
|
1708
1909
|
return stops.map((stop) => ({
|
|
1709
1910
|
...stop,
|
|
1710
1911
|
position: (stop.position - min) / range
|
|
1711
1912
|
}));
|
|
1712
1913
|
}
|
|
1914
|
+
function getDistanceToSide$1(center, width, height, side) {
|
|
1915
|
+
if (side === "left") return center.x;
|
|
1916
|
+
if (side === "right") return width - center.x;
|
|
1917
|
+
if (side === "top") return center.y;
|
|
1918
|
+
return height - center.y;
|
|
1919
|
+
}
|
|
1920
|
+
function getDistanceToCorner$1(center, corner) {
|
|
1921
|
+
const dx = corner.x - center.x;
|
|
1922
|
+
const dy = corner.y - center.y;
|
|
1923
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
1924
|
+
}
|
|
1925
|
+
function getCornerDeltas$1(center, width, height) {
|
|
1926
|
+
return [
|
|
1927
|
+
{
|
|
1928
|
+
dx: -center.x,
|
|
1929
|
+
dy: -center.y
|
|
1930
|
+
},
|
|
1931
|
+
{
|
|
1932
|
+
dx: width - center.x,
|
|
1933
|
+
dy: -center.y
|
|
1934
|
+
},
|
|
1935
|
+
{
|
|
1936
|
+
dx: -center.x,
|
|
1937
|
+
dy: height - center.y
|
|
1938
|
+
},
|
|
1939
|
+
{
|
|
1940
|
+
dx: width - center.x,
|
|
1941
|
+
dy: height - center.y
|
|
1942
|
+
}
|
|
1943
|
+
];
|
|
1944
|
+
}
|
|
1945
|
+
function scaleEllipseRadiiToCorner$1(radiusX, radiusY, dx, dy) {
|
|
1946
|
+
const safeRadiusX = Math.max(radiusX, 1e-4);
|
|
1947
|
+
const safeRadiusY = Math.max(radiusY, 1e-4);
|
|
1948
|
+
const scale = Math.sqrt(dx * dx / (safeRadiusX * safeRadiusX) + dy * dy / (safeRadiusY * safeRadiusY));
|
|
1949
|
+
return {
|
|
1950
|
+
x: safeRadiusX * scale,
|
|
1951
|
+
y: safeRadiusY * scale
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1713
1954
|
var ModuleTransformerRadialGradientToCanvas = class {
|
|
1714
1955
|
target = "canvas-2d";
|
|
1715
1956
|
gradientType = "radial-gradient";
|
|
1716
1957
|
to(input) {
|
|
1717
1958
|
const gradient = input;
|
|
1718
1959
|
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;
|
|
1960
|
+
const center = this._resolveCenter(gradient.config.position, width, height);
|
|
1961
|
+
const radii = this._resolveRadialRadii(gradient.config.size, gradient.config.shape, center, width, height);
|
|
1962
|
+
const maxVisibleT = getMaxVisibleRadialT(center, radii, width, height);
|
|
1963
|
+
const baseStops = resolveRenderableGradientStops(gradient, RADIAL_GRADIENT_SAMPLE_COUNT);
|
|
1964
|
+
const { min, max, stops } = getStopRange$1(gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, maxVisibleT) : baseStops);
|
|
1732
1965
|
let normalizedStops = stops;
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1966
|
+
let innerFactor = 0;
|
|
1967
|
+
let outerFactor = gradient.isRepeating ? maxVisibleT : 1;
|
|
1968
|
+
if (gradient.isRepeating) normalizedStops = normalizeStops$2(stops, 0, maxVisibleT);
|
|
1969
|
+
else if (min < 0 || max > 1) {
|
|
1970
|
+
normalizedStops = normalizeStops$2(stops, min, max);
|
|
1971
|
+
innerFactor = min;
|
|
1972
|
+
outerFactor = max;
|
|
1740
1973
|
}
|
|
1741
|
-
|
|
1974
|
+
if (gradient.config.shape === "circle") {
|
|
1975
|
+
const baseRadius = radii.x;
|
|
1976
|
+
const innerRadius = Math.max(0, baseRadius * innerFactor);
|
|
1977
|
+
const outerRadius = Math.max(innerRadius + 1e-4, baseRadius * outerFactor);
|
|
1978
|
+
const g = ctx.createRadialGradient(center.x, center.y, innerRadius, center.x, center.y, outerRadius);
|
|
1979
|
+
for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor(stop.value));
|
|
1980
|
+
ctx.fillStyle = g;
|
|
1981
|
+
ctx.fillRect(0, 0, width, height);
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
const outerRadius = Math.max(radii.x, radii.y);
|
|
1985
|
+
const scaleX = radii.x / outerRadius;
|
|
1986
|
+
const scaleY = radii.y / outerRadius;
|
|
1987
|
+
const innerRadius = Math.max(0, outerRadius * innerFactor);
|
|
1988
|
+
const scaledOuterRadius = Math.max(innerRadius + 1e-4, outerRadius * outerFactor);
|
|
1989
|
+
ctx.save();
|
|
1990
|
+
ctx.translate(center.x, center.y);
|
|
1991
|
+
ctx.scale(scaleX, scaleY);
|
|
1992
|
+
const g = ctx.createRadialGradient(0, 0, innerRadius, 0, 0, scaledOuterRadius);
|
|
1742
1993
|
for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor(stop.value));
|
|
1743
1994
|
ctx.fillStyle = g;
|
|
1744
|
-
|
|
1995
|
+
const drawRadius = scaledOuterRadius + 2;
|
|
1996
|
+
ctx.fillRect(-drawRadius / scaleX * 2, -drawRadius / scaleY * 2, drawRadius / scaleX * 4, drawRadius / scaleY * 4);
|
|
1997
|
+
ctx.restore();
|
|
1745
1998
|
} };
|
|
1746
1999
|
}
|
|
2000
|
+
_resolveCenter(position, width, height) {
|
|
2001
|
+
if (position.kind === "keywords") return {
|
|
2002
|
+
x: this._resolveKeywordX(position.x, width),
|
|
2003
|
+
y: this._resolveKeywordY(position.y, height)
|
|
2004
|
+
};
|
|
2005
|
+
return {
|
|
2006
|
+
x: this._resolve(position.x, width),
|
|
2007
|
+
y: this._resolve(position.y, height)
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
_resolveKeywordX(value, width) {
|
|
2011
|
+
if (value === "left") return 0;
|
|
2012
|
+
if (value === "center") return width / 2;
|
|
2013
|
+
return width;
|
|
2014
|
+
}
|
|
2015
|
+
_resolveKeywordY(value, height) {
|
|
2016
|
+
if (value === "top") return 0;
|
|
2017
|
+
if (value === "center") return height / 2;
|
|
2018
|
+
return height;
|
|
2019
|
+
}
|
|
2020
|
+
_resolveRadialRadii(size, shape, center, width, height) {
|
|
2021
|
+
if (size.kind === "explicit") {
|
|
2022
|
+
const radiusX = this._resolve(size.x, width);
|
|
2023
|
+
const radiusY = size.y ? this._resolve(size.y, height) : radiusX;
|
|
2024
|
+
return {
|
|
2025
|
+
x: Math.max(radiusX, 1e-4),
|
|
2026
|
+
y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
const left = getDistanceToSide$1(center, width, height, "left");
|
|
2030
|
+
const right = getDistanceToSide$1(center, width, height, "right");
|
|
2031
|
+
const top = getDistanceToSide$1(center, width, height, "top");
|
|
2032
|
+
const bottom = getDistanceToSide$1(center, width, height, "bottom");
|
|
2033
|
+
if (shape === "circle") {
|
|
2034
|
+
const cornerDistances = [
|
|
2035
|
+
{
|
|
2036
|
+
x: 0,
|
|
2037
|
+
y: 0
|
|
2038
|
+
},
|
|
2039
|
+
{
|
|
2040
|
+
x: width,
|
|
2041
|
+
y: 0
|
|
2042
|
+
},
|
|
2043
|
+
{
|
|
2044
|
+
x: 0,
|
|
2045
|
+
y: height
|
|
2046
|
+
},
|
|
2047
|
+
{
|
|
2048
|
+
x: width,
|
|
2049
|
+
y: height
|
|
2050
|
+
}
|
|
2051
|
+
].map((corner) => getDistanceToCorner$1(center, corner));
|
|
2052
|
+
if (size.value === "closest-side") {
|
|
2053
|
+
const radius = Math.min(left, right, top, bottom);
|
|
2054
|
+
return {
|
|
2055
|
+
x: radius,
|
|
2056
|
+
y: radius
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
if (size.value === "farthest-side") {
|
|
2060
|
+
const radius = Math.max(left, right, top, bottom);
|
|
2061
|
+
return {
|
|
2062
|
+
x: radius,
|
|
2063
|
+
y: radius
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
if (size.value === "closest-corner") {
|
|
2067
|
+
const radius = Math.min(...cornerDistances);
|
|
2068
|
+
return {
|
|
2069
|
+
x: radius,
|
|
2070
|
+
y: radius
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
const radius = Math.max(...cornerDistances);
|
|
2074
|
+
return {
|
|
2075
|
+
x: radius,
|
|
2076
|
+
y: radius
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
const closestSideRadiusX = Math.min(left, right);
|
|
2080
|
+
const closestSideRadiusY = Math.min(top, bottom);
|
|
2081
|
+
const farthestSideRadiusX = Math.max(left, right);
|
|
2082
|
+
const farthestSideRadiusY = Math.max(top, bottom);
|
|
2083
|
+
if (size.value === "closest-side") return {
|
|
2084
|
+
x: Math.max(closestSideRadiusX, 1e-4),
|
|
2085
|
+
y: Math.max(closestSideRadiusY, 1e-4)
|
|
2086
|
+
};
|
|
2087
|
+
if (size.value === "farthest-side") return {
|
|
2088
|
+
x: Math.max(farthestSideRadiusX, 1e-4),
|
|
2089
|
+
y: Math.max(farthestSideRadiusY, 1e-4)
|
|
2090
|
+
};
|
|
2091
|
+
const corners = getCornerDeltas$1(center, width, height);
|
|
2092
|
+
if (size.value === "closest-corner") return corners.map((corner) => scaleEllipseRadiiToCorner$1(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => {
|
|
2093
|
+
const closestArea = closest.x * closest.y;
|
|
2094
|
+
return current.x * current.y < closestArea ? current : closest;
|
|
2095
|
+
});
|
|
2096
|
+
return corners.map((corner) => scaleEllipseRadiiToCorner$1(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => {
|
|
2097
|
+
const farthestArea = farthest.x * farthest.y;
|
|
2098
|
+
return current.x * current.y > farthestArea ? current : farthest;
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
1747
2101
|
_resolve(value, size) {
|
|
1748
2102
|
if (value.kind === "percent") return value.value / 100 * size;
|
|
1749
2103
|
if (value.unit === "px") return value.value;
|
|
@@ -1752,7 +2106,8 @@ var ModuleTransformerRadialGradientToCanvas = class {
|
|
|
1752
2106
|
};
|
|
1753
2107
|
//#endregion
|
|
1754
2108
|
//#region src/gradient-transformer/modules/canvas/ModuleTransformerConicGradientToCanvas.ts
|
|
1755
|
-
const
|
|
2109
|
+
const CONIC_GRADIENT_SAMPLE_COUNT = 128;
|
|
2110
|
+
const toRgb$3 = converter("rgb");
|
|
1756
2111
|
var ModuleTransformerConicGradientToCanvas = class {
|
|
1757
2112
|
target = "canvas-2d";
|
|
1758
2113
|
gradientType = "conic-gradient";
|
|
@@ -1763,7 +2118,8 @@ var ModuleTransformerConicGradientToCanvas = class {
|
|
|
1763
2118
|
const data = imageData.data;
|
|
1764
2119
|
const { x: cx, y: cy } = this._resolvePosition(gradient.config.position, width, height);
|
|
1765
2120
|
const from = this._toRad(gradient.config.from);
|
|
1766
|
-
const
|
|
2121
|
+
const renderStops = resolveRenderableGradientStops(gradient, CONIC_GRADIENT_SAMPLE_COUNT);
|
|
2122
|
+
const stops = this._normalizeStops(renderStops);
|
|
1767
2123
|
if (stops.length === 0) {
|
|
1768
2124
|
ctx.putImageData(imageData, 0, 0);
|
|
1769
2125
|
return;
|
|
@@ -1851,7 +2207,7 @@ var ModuleTransformerConicGradientToCanvas = class {
|
|
|
1851
2207
|
};
|
|
1852
2208
|
}
|
|
1853
2209
|
_parseColor(input) {
|
|
1854
|
-
const color = toRgb$
|
|
2210
|
+
const color = toRgb$3(input);
|
|
1855
2211
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
1856
2212
|
return {
|
|
1857
2213
|
r: Math.round((color.r ?? 0) * 255),
|
|
@@ -1866,10 +2222,10 @@ var ModuleTransformerConicGradientToCanvas = class {
|
|
|
1866
2222
|
};
|
|
1867
2223
|
//#endregion
|
|
1868
2224
|
//#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);
|
|
2225
|
+
const toRgb$2 = converter("rgb");
|
|
2226
|
+
const MAX_STOPS$2 = 128;
|
|
2227
|
+
function toWebGLColor$2(input) {
|
|
2228
|
+
const color = toRgb$2(input);
|
|
1873
2229
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
1874
2230
|
return [
|
|
1875
2231
|
color.r ?? 0,
|
|
@@ -1891,14 +2247,14 @@ function getStopRange(stops) {
|
|
|
1891
2247
|
stops: colorStops
|
|
1892
2248
|
};
|
|
1893
2249
|
}
|
|
1894
|
-
function normalizeStops(stops, min, max) {
|
|
2250
|
+
function normalizeStops$1(stops, min, max) {
|
|
1895
2251
|
const range = max - min || 1;
|
|
1896
2252
|
return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
|
|
1897
2253
|
...stop,
|
|
1898
2254
|
position: (stop.position - min) / range
|
|
1899
2255
|
}));
|
|
1900
2256
|
}
|
|
1901
|
-
function createShader(gl, type, source) {
|
|
2257
|
+
function createShader$2(gl, type, source) {
|
|
1902
2258
|
const shader = gl.createShader(type);
|
|
1903
2259
|
if (!shader) throw new Error("Failed to create WebGL shader.");
|
|
1904
2260
|
gl.shaderSource(shader, source);
|
|
@@ -1910,9 +2266,9 @@ function createShader(gl, type, source) {
|
|
|
1910
2266
|
}
|
|
1911
2267
|
return shader;
|
|
1912
2268
|
}
|
|
1913
|
-
function createProgram(gl, vertexSource, fragmentSource) {
|
|
1914
|
-
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
1915
|
-
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
2269
|
+
function createProgram$2(gl, vertexSource, fragmentSource) {
|
|
2270
|
+
const vertexShader = createShader$2(gl, gl.VERTEX_SHADER, vertexSource);
|
|
2271
|
+
const fragmentShader = createShader$2(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
1916
2272
|
const program = gl.createProgram();
|
|
1917
2273
|
if (!program) throw new Error("Failed to create WebGL program.");
|
|
1918
2274
|
gl.attachShader(program, vertexShader);
|
|
@@ -1925,15 +2281,15 @@ function createProgram(gl, vertexSource, fragmentSource) {
|
|
|
1925
2281
|
}
|
|
1926
2282
|
return program;
|
|
1927
2283
|
}
|
|
1928
|
-
function getColorStopCount(stops) {
|
|
2284
|
+
function getColorStopCount$2(stops) {
|
|
1929
2285
|
return stops.filter((stop) => stop.type === "color-stop").length;
|
|
1930
2286
|
}
|
|
1931
|
-
function getWebGLSampleCount(gradient, maxStops) {
|
|
1932
|
-
const colorStopCount = getColorStopCount(gradient.stops);
|
|
2287
|
+
function getWebGLSampleCount$2(gradient, maxStops) {
|
|
2288
|
+
const colorStopCount = getColorStopCount$2(gradient.stops);
|
|
1933
2289
|
const segmentCount = Math.max(1, colorStopCount - 1);
|
|
1934
2290
|
return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
|
|
1935
2291
|
}
|
|
1936
|
-
function getColorAtPosition(stops, position) {
|
|
2292
|
+
function getColorAtPosition$2(stops, position) {
|
|
1937
2293
|
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
1938
2294
|
if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
|
|
1939
2295
|
if (position <= colorStops[0].position) return colorStops[0].value;
|
|
@@ -1950,7 +2306,7 @@ function getColorAtPosition(stops, position) {
|
|
|
1950
2306
|
}
|
|
1951
2307
|
return lastStop.value;
|
|
1952
2308
|
}
|
|
1953
|
-
function fitStopsToWebGLLimit(stops, maxStops) {
|
|
2309
|
+
function fitStopsToWebGLLimit$2(stops, maxStops) {
|
|
1954
2310
|
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
1955
2311
|
if (colorStops.length <= maxStops) return colorStops;
|
|
1956
2312
|
const sampledStops = [];
|
|
@@ -1958,7 +2314,7 @@ function fitStopsToWebGLLimit(stops, maxStops) {
|
|
|
1958
2314
|
const position = index / (maxStops - 1);
|
|
1959
2315
|
sampledStops.push({
|
|
1960
2316
|
type: "color-stop",
|
|
1961
|
-
value: getColorAtPosition(colorStops, position),
|
|
2317
|
+
value: getColorAtPosition$2(colorStops, position),
|
|
1962
2318
|
position
|
|
1963
2319
|
});
|
|
1964
2320
|
}
|
|
@@ -1975,7 +2331,7 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
1975
2331
|
canvas.width = width;
|
|
1976
2332
|
canvas.height = height;
|
|
1977
2333
|
gl.viewport(0, 0, width, height);
|
|
1978
|
-
const program = createProgram(gl, `
|
|
2334
|
+
const program = createProgram$2(gl, `
|
|
1979
2335
|
attribute vec2 a_position;
|
|
1980
2336
|
varying vec2 v_uv;
|
|
1981
2337
|
|
|
@@ -1991,13 +2347,13 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
1991
2347
|
uniform vec2 u_start;
|
|
1992
2348
|
uniform vec2 u_end;
|
|
1993
2349
|
uniform int u_stopCount;
|
|
1994
|
-
uniform float u_positions[${MAX_STOPS}];
|
|
1995
|
-
uniform vec4 u_colors[${MAX_STOPS}];
|
|
2350
|
+
uniform float u_positions[${MAX_STOPS$2}];
|
|
2351
|
+
uniform vec4 u_colors[${MAX_STOPS$2}];
|
|
1996
2352
|
|
|
1997
2353
|
vec4 getGradientColor(float t) {
|
|
1998
2354
|
vec4 result = u_colors[0];
|
|
1999
2355
|
|
|
2000
|
-
for (int i = 0; i < ${MAX_STOPS - 1}; i++) {
|
|
2356
|
+
for (int i = 0; i < ${MAX_STOPS$2 - 1}; i++) {
|
|
2001
2357
|
if (i >= u_stopCount - 1) {
|
|
2002
2358
|
break;
|
|
2003
2359
|
}
|
|
@@ -2062,7 +2418,7 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
2062
2418
|
let startY = centerY - dirY * lineLength / 2;
|
|
2063
2419
|
let endX = centerX + dirX * lineLength / 2;
|
|
2064
2420
|
let endY = centerY + dirY * lineLength / 2;
|
|
2065
|
-
const { min, max, stops } = getStopRange(
|
|
2421
|
+
const { min, max, stops } = getStopRange(resolveRenderableGradientStops(gradient, getWebGLSampleCount$2(gradient, MAX_STOPS$2)));
|
|
2066
2422
|
let normalizedStops = stops;
|
|
2067
2423
|
if (min < 0 || max > 1) {
|
|
2068
2424
|
const vx = endX - startX;
|
|
@@ -2073,17 +2429,17 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
2073
2429
|
startY = baseStartY + vy * min;
|
|
2074
2430
|
endX = baseStartX + vx * max;
|
|
2075
2431
|
endY = baseStartY + vy * max;
|
|
2076
|
-
normalizedStops = normalizeStops(stops, min, max);
|
|
2432
|
+
normalizedStops = normalizeStops$1(stops, min, max);
|
|
2077
2433
|
}
|
|
2078
2434
|
const startU = startX / width;
|
|
2079
2435
|
const startV = 1 - startY / height;
|
|
2080
2436
|
const endU = endX / width;
|
|
2081
2437
|
const endV = 1 - endY / height;
|
|
2082
|
-
const limitedStops = fitStopsToWebGLLimit(normalizedStops, MAX_STOPS);
|
|
2083
|
-
const positions = new Float32Array(MAX_STOPS);
|
|
2084
|
-
const colors = new Float32Array(MAX_STOPS * 4);
|
|
2438
|
+
const limitedStops = fitStopsToWebGLLimit$2(normalizedStops, MAX_STOPS$2);
|
|
2439
|
+
const positions = new Float32Array(MAX_STOPS$2);
|
|
2440
|
+
const colors = new Float32Array(MAX_STOPS$2 * 4);
|
|
2085
2441
|
limitedStops.forEach((stop, index) => {
|
|
2086
|
-
const color = toWebGLColor(stop.value);
|
|
2442
|
+
const color = toWebGLColor$2(stop.value);
|
|
2087
2443
|
positions[index] = stop.position;
|
|
2088
2444
|
colors[index * 4 + 0] = color[0];
|
|
2089
2445
|
colors[index * 4 + 1] = color[1];
|
|
@@ -2102,6 +2458,602 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
2102
2458
|
}
|
|
2103
2459
|
};
|
|
2104
2460
|
//#endregion
|
|
2461
|
+
//#region src/gradient-transformer/modules/webgl/ModuleTransformerConicGradientToWebgl.ts
|
|
2462
|
+
const toRgb$1 = converter("rgb");
|
|
2463
|
+
const MAX_STOPS$1 = 128;
|
|
2464
|
+
const TWO_PI = Math.PI * 2;
|
|
2465
|
+
function toWebGLColor$1(input) {
|
|
2466
|
+
const color = toRgb$1(input);
|
|
2467
|
+
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
2468
|
+
return [
|
|
2469
|
+
color.r ?? 0,
|
|
2470
|
+
color.g ?? 0,
|
|
2471
|
+
color.b ?? 0,
|
|
2472
|
+
color.alpha ?? 1
|
|
2473
|
+
];
|
|
2474
|
+
}
|
|
2475
|
+
function createShader$1(gl, type, source) {
|
|
2476
|
+
const shader = gl.createShader(type);
|
|
2477
|
+
if (!shader) throw new Error("Failed to create WebGL shader.");
|
|
2478
|
+
gl.shaderSource(shader, source);
|
|
2479
|
+
gl.compileShader(shader);
|
|
2480
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
2481
|
+
const error = gl.getShaderInfoLog(shader);
|
|
2482
|
+
gl.deleteShader(shader);
|
|
2483
|
+
throw new Error(`WebGL shader compile error: ${error}`);
|
|
2484
|
+
}
|
|
2485
|
+
return shader;
|
|
2486
|
+
}
|
|
2487
|
+
function createProgram$1(gl, vertexSource, fragmentSource) {
|
|
2488
|
+
const vertexShader = createShader$1(gl, gl.VERTEX_SHADER, vertexSource);
|
|
2489
|
+
const fragmentShader = createShader$1(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
2490
|
+
const program = gl.createProgram();
|
|
2491
|
+
if (!program) throw new Error("Failed to create WebGL program.");
|
|
2492
|
+
gl.attachShader(program, vertexShader);
|
|
2493
|
+
gl.attachShader(program, fragmentShader);
|
|
2494
|
+
gl.linkProgram(program);
|
|
2495
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
2496
|
+
const error = gl.getProgramInfoLog(program);
|
|
2497
|
+
gl.deleteProgram(program);
|
|
2498
|
+
throw new Error(`WebGL program link error: ${error}`);
|
|
2499
|
+
}
|
|
2500
|
+
return program;
|
|
2501
|
+
}
|
|
2502
|
+
function getColorStopCount$1(stops) {
|
|
2503
|
+
return stops.filter((stop) => stop.type === "color-stop").length;
|
|
2504
|
+
}
|
|
2505
|
+
function getWebGLSampleCount$1(gradient, maxStops) {
|
|
2506
|
+
const colorStopCount = getColorStopCount$1(gradient.stops);
|
|
2507
|
+
const segmentCount = Math.max(1, colorStopCount - 1);
|
|
2508
|
+
return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
|
|
2509
|
+
}
|
|
2510
|
+
function getColorAtPosition$1(stops, position) {
|
|
2511
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
2512
|
+
if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
|
|
2513
|
+
if (position <= colorStops[0].position) return colorStops[0].value;
|
|
2514
|
+
const lastStop = colorStops[colorStops.length - 1];
|
|
2515
|
+
if (position >= lastStop.position) return lastStop.value;
|
|
2516
|
+
for (let index = 0; index < colorStops.length - 1; index += 1) {
|
|
2517
|
+
const current = colorStops[index];
|
|
2518
|
+
const next = colorStops[index + 1];
|
|
2519
|
+
if (position >= current.position && position <= next.position) {
|
|
2520
|
+
const range = next.position - current.position || 1;
|
|
2521
|
+
const localT = (position - current.position) / range;
|
|
2522
|
+
return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
return lastStop.value;
|
|
2526
|
+
}
|
|
2527
|
+
function fitStopsToWebGLLimit$1(stops, maxStops) {
|
|
2528
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
2529
|
+
if (colorStops.length <= maxStops) return colorStops;
|
|
2530
|
+
const sampledStops = [];
|
|
2531
|
+
for (let index = 0; index < maxStops; index += 1) {
|
|
2532
|
+
const position = index / (maxStops - 1);
|
|
2533
|
+
sampledStops.push({
|
|
2534
|
+
type: "color-stop",
|
|
2535
|
+
value: getColorAtPosition$1(colorStops, position),
|
|
2536
|
+
position
|
|
2537
|
+
});
|
|
2538
|
+
}
|
|
2539
|
+
return sampledStops;
|
|
2540
|
+
}
|
|
2541
|
+
function resolveLengthPercentage(value, reference) {
|
|
2542
|
+
if (value.kind === "percent") return value.value / 100 * reference;
|
|
2543
|
+
if (value.kind === "length") {
|
|
2544
|
+
if (value.unit === "px") return value.value;
|
|
2545
|
+
throw new Error(`Unsupported gradient length unit for WebGL conic gradient: ${value.unit}`);
|
|
2546
|
+
}
|
|
2547
|
+
return value;
|
|
2548
|
+
}
|
|
2549
|
+
function resolveAngleToRadians(angle) {
|
|
2550
|
+
if (angle.unit === "rad") return angle.value;
|
|
2551
|
+
if (angle.unit === "deg") return angle.value / 360 * TWO_PI;
|
|
2552
|
+
if (angle.unit === "turn") return angle.value * TWO_PI;
|
|
2553
|
+
if (angle.unit === "grad") return angle.value / 400 * TWO_PI;
|
|
2554
|
+
return angle.unit;
|
|
2555
|
+
}
|
|
2556
|
+
function resolveKeywordPositionX$1(x, width) {
|
|
2557
|
+
if (x === "left") return 0;
|
|
2558
|
+
if (x === "center") return width / 2;
|
|
2559
|
+
if (x === "right") return width;
|
|
2560
|
+
return width / 2;
|
|
2561
|
+
}
|
|
2562
|
+
function resolveKeywordPositionY$1(y, height) {
|
|
2563
|
+
if (y === "top") return 0;
|
|
2564
|
+
if (y === "center") return height / 2;
|
|
2565
|
+
if (y === "bottom") return height;
|
|
2566
|
+
return height / 2;
|
|
2567
|
+
}
|
|
2568
|
+
function resolveConicCenter(position, width, height) {
|
|
2569
|
+
if (position.kind === "keywords") return {
|
|
2570
|
+
x: resolveKeywordPositionX$1(position.x, width),
|
|
2571
|
+
y: resolveKeywordPositionY$1(position.y, height)
|
|
2572
|
+
};
|
|
2573
|
+
if (position.kind === "values") return {
|
|
2574
|
+
x: resolveLengthPercentage(position.x, width),
|
|
2575
|
+
y: resolveLengthPercentage(position.y, height)
|
|
2576
|
+
};
|
|
2577
|
+
return {
|
|
2578
|
+
x: width / 2,
|
|
2579
|
+
y: height / 2
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
var ModuleTransformerConicGradientToCanvasWebGL = class {
|
|
2583
|
+
target = "canvas-webgl";
|
|
2584
|
+
gradientType = "conic-gradient";
|
|
2585
|
+
to(input) {
|
|
2586
|
+
const gradient = input;
|
|
2587
|
+
return { draw: (canvas, width, height) => {
|
|
2588
|
+
const gl = canvas.getContext("webgl");
|
|
2589
|
+
if (!gl) throw new Error("WebGL is not supported.");
|
|
2590
|
+
canvas.width = width;
|
|
2591
|
+
canvas.height = height;
|
|
2592
|
+
gl.viewport(0, 0, width, height);
|
|
2593
|
+
const program = createProgram$1(gl, `
|
|
2594
|
+
attribute vec2 a_position;
|
|
2595
|
+
varying vec2 v_uv;
|
|
2596
|
+
|
|
2597
|
+
void main() {
|
|
2598
|
+
v_uv = a_position * 0.5 + 0.5;
|
|
2599
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
2600
|
+
}
|
|
2601
|
+
`, `
|
|
2602
|
+
precision mediump float;
|
|
2603
|
+
|
|
2604
|
+
const float PI = 3.141592653589793;
|
|
2605
|
+
const float TWO_PI = 6.283185307179586;
|
|
2606
|
+
|
|
2607
|
+
varying vec2 v_uv;
|
|
2608
|
+
|
|
2609
|
+
uniform vec2 u_center;
|
|
2610
|
+
uniform float u_startAngle;
|
|
2611
|
+
uniform int u_stopCount;
|
|
2612
|
+
uniform float u_positions[${MAX_STOPS$1}];
|
|
2613
|
+
uniform vec4 u_colors[${MAX_STOPS$1}];
|
|
2614
|
+
|
|
2615
|
+
vec4 getGradientColor(float t) {
|
|
2616
|
+
vec4 result = u_colors[0];
|
|
2617
|
+
|
|
2618
|
+
for (int i = 0; i < ${MAX_STOPS$1 - 1}; i++) {
|
|
2619
|
+
if (i >= u_stopCount - 1) {
|
|
2620
|
+
break;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
float currentPosition = u_positions[i];
|
|
2624
|
+
float nextPosition = u_positions[i + 1];
|
|
2625
|
+
|
|
2626
|
+
if (t <= currentPosition) {
|
|
2627
|
+
return u_colors[i];
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
if (t >= currentPosition && t <= nextPosition) {
|
|
2631
|
+
float localT = (t - currentPosition) / max(nextPosition - currentPosition, 0.00001);
|
|
2632
|
+
return mix(u_colors[i], u_colors[i + 1], localT);
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
result = u_colors[i + 1];
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
return result;
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
void main() {
|
|
2642
|
+
vec2 delta = v_uv - u_center;
|
|
2643
|
+
|
|
2644
|
+
float angle = atan(delta.y, delta.x);
|
|
2645
|
+
float cssAngle = mod((PI * 0.5) - angle + TWO_PI, TWO_PI);
|
|
2646
|
+
|
|
2647
|
+
float t = mod(cssAngle - u_startAngle + TWO_PI, TWO_PI) / TWO_PI;
|
|
2648
|
+
|
|
2649
|
+
gl_FragColor = getGradientColor(t);
|
|
2650
|
+
}
|
|
2651
|
+
`);
|
|
2652
|
+
gl.useProgram(program);
|
|
2653
|
+
const positionBuffer = gl.createBuffer();
|
|
2654
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
2655
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
2656
|
+
-1,
|
|
2657
|
+
-1,
|
|
2658
|
+
1,
|
|
2659
|
+
-1,
|
|
2660
|
+
-1,
|
|
2661
|
+
1,
|
|
2662
|
+
-1,
|
|
2663
|
+
1,
|
|
2664
|
+
1,
|
|
2665
|
+
-1,
|
|
2666
|
+
1,
|
|
2667
|
+
1
|
|
2668
|
+
]), gl.STATIC_DRAW);
|
|
2669
|
+
const positionLocation = gl.getAttribLocation(program, "a_position");
|
|
2670
|
+
gl.enableVertexAttribArray(positionLocation);
|
|
2671
|
+
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
2672
|
+
const center = resolveConicCenter(gradient.config.position, width, height);
|
|
2673
|
+
const limitedStops = fitStopsToWebGLLimit$1(resolveRenderableGradientStops(gradient, getWebGLSampleCount$1(gradient, MAX_STOPS$1)), MAX_STOPS$1);
|
|
2674
|
+
const positions = new Float32Array(MAX_STOPS$1);
|
|
2675
|
+
const colors = new Float32Array(MAX_STOPS$1 * 4);
|
|
2676
|
+
limitedStops.forEach((stop, index) => {
|
|
2677
|
+
const color = toWebGLColor$1(stop.value);
|
|
2678
|
+
positions[index] = stop.position;
|
|
2679
|
+
colors[index * 4 + 0] = color[0];
|
|
2680
|
+
colors[index * 4 + 1] = color[1];
|
|
2681
|
+
colors[index * 4 + 2] = color[2];
|
|
2682
|
+
colors[index * 4 + 3] = color[3];
|
|
2683
|
+
});
|
|
2684
|
+
gl.uniform2f(gl.getUniformLocation(program, "u_center"), center.x / width, 1 - center.y / height);
|
|
2685
|
+
gl.uniform1f(gl.getUniformLocation(program, "u_startAngle"), resolveAngleToRadians(gradient.config.from));
|
|
2686
|
+
gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
|
|
2687
|
+
gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
|
|
2688
|
+
gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
|
|
2689
|
+
gl.clearColor(0, 0, 0, 0);
|
|
2690
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
2691
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
2692
|
+
} };
|
|
2693
|
+
}
|
|
2694
|
+
};
|
|
2695
|
+
//#endregion
|
|
2696
|
+
//#region src/gradient-transformer/modules/webgl/ModuleTransformerRadialGradientToWebgl.ts
|
|
2697
|
+
const toRgb = converter("rgb");
|
|
2698
|
+
const MAX_STOPS = 128;
|
|
2699
|
+
const MAX_REPEATING_RADIAL_T = 16;
|
|
2700
|
+
function toWebGLColor(input) {
|
|
2701
|
+
const color = toRgb(input);
|
|
2702
|
+
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
2703
|
+
return [
|
|
2704
|
+
color.r ?? 0,
|
|
2705
|
+
color.g ?? 0,
|
|
2706
|
+
color.b ?? 0,
|
|
2707
|
+
color.alpha ?? 1
|
|
2708
|
+
];
|
|
2709
|
+
}
|
|
2710
|
+
function createShader(gl, type, source) {
|
|
2711
|
+
const shader = gl.createShader(type);
|
|
2712
|
+
if (!shader) throw new Error("Failed to create WebGL shader.");
|
|
2713
|
+
gl.shaderSource(shader, source);
|
|
2714
|
+
gl.compileShader(shader);
|
|
2715
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
2716
|
+
const error = gl.getShaderInfoLog(shader);
|
|
2717
|
+
gl.deleteShader(shader);
|
|
2718
|
+
throw new Error(`WebGL shader compile error: ${error}`);
|
|
2719
|
+
}
|
|
2720
|
+
return shader;
|
|
2721
|
+
}
|
|
2722
|
+
function createProgram(gl, vertexSource, fragmentSource) {
|
|
2723
|
+
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
2724
|
+
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
2725
|
+
const program = gl.createProgram();
|
|
2726
|
+
if (!program) throw new Error("Failed to create WebGL program.");
|
|
2727
|
+
gl.attachShader(program, vertexShader);
|
|
2728
|
+
gl.attachShader(program, fragmentShader);
|
|
2729
|
+
gl.linkProgram(program);
|
|
2730
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
2731
|
+
const error = gl.getProgramInfoLog(program);
|
|
2732
|
+
gl.deleteProgram(program);
|
|
2733
|
+
throw new Error(`WebGL program link error: ${error}`);
|
|
2734
|
+
}
|
|
2735
|
+
return program;
|
|
2736
|
+
}
|
|
2737
|
+
function getColorStopCount(stops) {
|
|
2738
|
+
return stops.filter((stop) => stop.type === "color-stop").length;
|
|
2739
|
+
}
|
|
2740
|
+
function getWebGLSampleCount(gradient, maxStops) {
|
|
2741
|
+
const colorStopCount = getColorStopCount(gradient.stops);
|
|
2742
|
+
const segmentCount = Math.max(1, colorStopCount - 1);
|
|
2743
|
+
return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
|
|
2744
|
+
}
|
|
2745
|
+
function getColorAtPosition(stops, position) {
|
|
2746
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
2747
|
+
if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
|
|
2748
|
+
if (position <= colorStops[0].position) return colorStops[0].value;
|
|
2749
|
+
const lastStop = colorStops[colorStops.length - 1];
|
|
2750
|
+
if (position >= lastStop.position) return lastStop.value;
|
|
2751
|
+
for (let index = 0; index < colorStops.length - 1; index += 1) {
|
|
2752
|
+
const current = colorStops[index];
|
|
2753
|
+
const next = colorStops[index + 1];
|
|
2754
|
+
if (position >= current.position && position <= next.position) {
|
|
2755
|
+
const range = next.position - current.position || 1;
|
|
2756
|
+
const localT = (position - current.position) / range;
|
|
2757
|
+
return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
return lastStop.value;
|
|
2761
|
+
}
|
|
2762
|
+
function fitStopsToWebGLLimit(stops, maxStops) {
|
|
2763
|
+
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
2764
|
+
if (colorStops.length <= maxStops) return colorStops;
|
|
2765
|
+
const sampledStops = [];
|
|
2766
|
+
for (let index = 0; index < maxStops; index += 1) {
|
|
2767
|
+
const position = index / (maxStops - 1);
|
|
2768
|
+
sampledStops.push({
|
|
2769
|
+
type: "color-stop",
|
|
2770
|
+
value: getColorAtPosition(colorStops, position),
|
|
2771
|
+
position
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
return sampledStops;
|
|
2775
|
+
}
|
|
2776
|
+
function parseLengthPercentage(value, reference) {
|
|
2777
|
+
if (value.kind === "percent") return value.value / 100 * reference;
|
|
2778
|
+
if (value.kind === "length") {
|
|
2779
|
+
if (value.unit === "px") return value.value;
|
|
2780
|
+
throw new Error(`Unsupported gradient length unit for WebGL radial gradient: ${value.unit}`);
|
|
2781
|
+
}
|
|
2782
|
+
return value;
|
|
2783
|
+
}
|
|
2784
|
+
function resolveKeywordPositionX(x, width) {
|
|
2785
|
+
if (x === "left") return 0;
|
|
2786
|
+
if (x === "center") return width / 2;
|
|
2787
|
+
if (x === "right") return width;
|
|
2788
|
+
return width / 2;
|
|
2789
|
+
}
|
|
2790
|
+
function resolveKeywordPositionY(y, height) {
|
|
2791
|
+
if (y === "top") return 0;
|
|
2792
|
+
if (y === "center") return height / 2;
|
|
2793
|
+
if (y === "bottom") return height;
|
|
2794
|
+
return height / 2;
|
|
2795
|
+
}
|
|
2796
|
+
function resolveRadialCenter(position, width, height) {
|
|
2797
|
+
if (position.kind === "keywords") return {
|
|
2798
|
+
x: resolveKeywordPositionX(position.x, width),
|
|
2799
|
+
y: resolveKeywordPositionY(position.y, height)
|
|
2800
|
+
};
|
|
2801
|
+
if (position.kind === "values") return {
|
|
2802
|
+
x: parseLengthPercentage(position.x, width),
|
|
2803
|
+
y: parseLengthPercentage(position.y, height)
|
|
2804
|
+
};
|
|
2805
|
+
return {
|
|
2806
|
+
x: width / 2,
|
|
2807
|
+
y: height / 2
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
function getDistanceToSide(center, width, height, side) {
|
|
2811
|
+
if (side === "left") return center.x;
|
|
2812
|
+
if (side === "right") return width - center.x;
|
|
2813
|
+
if (side === "top") return center.y;
|
|
2814
|
+
return height - center.y;
|
|
2815
|
+
}
|
|
2816
|
+
function getDistanceToCorner(center, corner) {
|
|
2817
|
+
const dx = corner.x - center.x;
|
|
2818
|
+
const dy = corner.y - center.y;
|
|
2819
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
2820
|
+
}
|
|
2821
|
+
function normalizeStops(stops, min, max) {
|
|
2822
|
+
const range = max - min || 1;
|
|
2823
|
+
return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
|
|
2824
|
+
...stop,
|
|
2825
|
+
position: (stop.position - min) / range
|
|
2826
|
+
}));
|
|
2827
|
+
}
|
|
2828
|
+
function getCornerDeltas(center, width, height) {
|
|
2829
|
+
return [
|
|
2830
|
+
{
|
|
2831
|
+
dx: -center.x,
|
|
2832
|
+
dy: -center.y
|
|
2833
|
+
},
|
|
2834
|
+
{
|
|
2835
|
+
dx: width - center.x,
|
|
2836
|
+
dy: -center.y
|
|
2837
|
+
},
|
|
2838
|
+
{
|
|
2839
|
+
dx: -center.x,
|
|
2840
|
+
dy: height - center.y
|
|
2841
|
+
},
|
|
2842
|
+
{
|
|
2843
|
+
dx: width - center.x,
|
|
2844
|
+
dy: height - center.y
|
|
2845
|
+
}
|
|
2846
|
+
];
|
|
2847
|
+
}
|
|
2848
|
+
function scaleEllipseRadiiToCorner(radiusX, radiusY, dx, dy) {
|
|
2849
|
+
const safeRadiusX = Math.max(radiusX, 1e-4);
|
|
2850
|
+
const safeRadiusY = Math.max(radiusY, 1e-4);
|
|
2851
|
+
const scale = Math.sqrt(dx * dx / (safeRadiusX * safeRadiusX) + dy * dy / (safeRadiusY * safeRadiusY));
|
|
2852
|
+
return {
|
|
2853
|
+
x: safeRadiusX * scale,
|
|
2854
|
+
y: safeRadiusY * scale
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
function resolveRadialRadii(size, shape, center, width, height) {
|
|
2858
|
+
if (size.kind === "explicit") {
|
|
2859
|
+
const radiusX = parseLengthPercentage(size.x, width);
|
|
2860
|
+
const radiusY = size.y ? parseLengthPercentage(size.y, height) : radiusX;
|
|
2861
|
+
return {
|
|
2862
|
+
x: Math.max(radiusX, 1e-4),
|
|
2863
|
+
y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
const left = getDistanceToSide(center, width, height, "left");
|
|
2867
|
+
const right = getDistanceToSide(center, width, height, "right");
|
|
2868
|
+
const top = getDistanceToSide(center, width, height, "top");
|
|
2869
|
+
const bottom = getDistanceToSide(center, width, height, "bottom");
|
|
2870
|
+
if (shape === "circle") {
|
|
2871
|
+
const cornerDistances = [
|
|
2872
|
+
{
|
|
2873
|
+
x: 0,
|
|
2874
|
+
y: 0
|
|
2875
|
+
},
|
|
2876
|
+
{
|
|
2877
|
+
x: width,
|
|
2878
|
+
y: 0
|
|
2879
|
+
},
|
|
2880
|
+
{
|
|
2881
|
+
x: 0,
|
|
2882
|
+
y: height
|
|
2883
|
+
},
|
|
2884
|
+
{
|
|
2885
|
+
x: width,
|
|
2886
|
+
y: height
|
|
2887
|
+
}
|
|
2888
|
+
].map((corner) => getDistanceToCorner(center, corner));
|
|
2889
|
+
if (size.value === "closest-side") {
|
|
2890
|
+
const radius = Math.max(Math.min(left, right, top, bottom), 1e-4);
|
|
2891
|
+
return {
|
|
2892
|
+
x: radius,
|
|
2893
|
+
y: radius
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
if (size.value === "farthest-side") {
|
|
2897
|
+
const radius = Math.max(Math.max(left, right, top, bottom), 1e-4);
|
|
2898
|
+
return {
|
|
2899
|
+
x: radius,
|
|
2900
|
+
y: radius
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2903
|
+
if (size.value === "closest-corner") {
|
|
2904
|
+
const radius = Math.max(Math.min(...cornerDistances), 1e-4);
|
|
2905
|
+
return {
|
|
2906
|
+
x: radius,
|
|
2907
|
+
y: radius
|
|
2908
|
+
};
|
|
2909
|
+
}
|
|
2910
|
+
const radius = Math.max(Math.max(...cornerDistances), 1e-4);
|
|
2911
|
+
return {
|
|
2912
|
+
x: radius,
|
|
2913
|
+
y: radius
|
|
2914
|
+
};
|
|
2915
|
+
}
|
|
2916
|
+
const closestSideRadiusX = Math.min(left, right);
|
|
2917
|
+
const closestSideRadiusY = Math.min(top, bottom);
|
|
2918
|
+
const farthestSideRadiusX = Math.max(left, right);
|
|
2919
|
+
const farthestSideRadiusY = Math.max(top, bottom);
|
|
2920
|
+
if (size.value === "closest-side") return {
|
|
2921
|
+
x: Math.max(closestSideRadiusX, 1e-4),
|
|
2922
|
+
y: Math.max(closestSideRadiusY, 1e-4)
|
|
2923
|
+
};
|
|
2924
|
+
if (size.value === "farthest-side") return {
|
|
2925
|
+
x: Math.max(farthestSideRadiusX, 1e-4),
|
|
2926
|
+
y: Math.max(farthestSideRadiusY, 1e-4)
|
|
2927
|
+
};
|
|
2928
|
+
const corners = getCornerDeltas(center, width, height);
|
|
2929
|
+
if (size.value === "closest-corner") return corners.map((corner) => scaleEllipseRadiiToCorner(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => {
|
|
2930
|
+
const closestArea = closest.x * closest.y;
|
|
2931
|
+
return current.x * current.y < closestArea ? current : closest;
|
|
2932
|
+
});
|
|
2933
|
+
return corners.map((corner) => scaleEllipseRadiiToCorner(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => {
|
|
2934
|
+
const farthestArea = farthest.x * farthest.y;
|
|
2935
|
+
return current.x * current.y > farthestArea ? current : farthest;
|
|
2936
|
+
});
|
|
2937
|
+
}
|
|
2938
|
+
var ModuleTransformerRadialGradientToCanvasWebGL = class {
|
|
2939
|
+
target = "canvas-webgl";
|
|
2940
|
+
gradientType = "radial-gradient";
|
|
2941
|
+
to(input) {
|
|
2942
|
+
const gradient = input;
|
|
2943
|
+
return { draw: (canvas, width, height) => {
|
|
2944
|
+
const gl = canvas.getContext("webgl");
|
|
2945
|
+
if (!gl) throw new Error("WebGL is not supported.");
|
|
2946
|
+
canvas.width = width;
|
|
2947
|
+
canvas.height = height;
|
|
2948
|
+
gl.viewport(0, 0, width, height);
|
|
2949
|
+
const program = createProgram(gl, `
|
|
2950
|
+
attribute vec2 a_position;
|
|
2951
|
+
varying vec2 v_uv;
|
|
2952
|
+
|
|
2953
|
+
void main() {
|
|
2954
|
+
v_uv = a_position * 0.5 + 0.5;
|
|
2955
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
2956
|
+
}
|
|
2957
|
+
`, `
|
|
2958
|
+
precision mediump float;
|
|
2959
|
+
|
|
2960
|
+
varying vec2 v_uv;
|
|
2961
|
+
|
|
2962
|
+
uniform vec2 u_center;
|
|
2963
|
+
uniform vec2 u_radius;
|
|
2964
|
+
uniform int u_stopCount;
|
|
2965
|
+
uniform float u_positions[${MAX_STOPS}];
|
|
2966
|
+
uniform vec4 u_colors[${MAX_STOPS}];
|
|
2967
|
+
uniform float u_tMax;
|
|
2968
|
+
|
|
2969
|
+
vec4 getGradientColor(float t) {
|
|
2970
|
+
vec4 result = u_colors[0];
|
|
2971
|
+
|
|
2972
|
+
for (int i = 0; i < ${MAX_STOPS - 1}; i++) {
|
|
2973
|
+
if (i >= u_stopCount - 1) {
|
|
2974
|
+
break;
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
float currentPosition = u_positions[i];
|
|
2978
|
+
float nextPosition = u_positions[i + 1];
|
|
2979
|
+
|
|
2980
|
+
if (t <= currentPosition) {
|
|
2981
|
+
return u_colors[i];
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
if (t >= currentPosition && t <= nextPosition) {
|
|
2985
|
+
float localT = (t - currentPosition) / max(nextPosition - currentPosition, 0.00001);
|
|
2986
|
+
return mix(u_colors[i], u_colors[i + 1], localT);
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
result = u_colors[i + 1];
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
return result;
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
void main() {
|
|
2996
|
+
vec2 delta = v_uv - u_center;
|
|
2997
|
+
vec2 normalized = delta / max(u_radius, vec2(0.00001));
|
|
2998
|
+
float t = length(normalized);
|
|
2999
|
+
|
|
3000
|
+
t = clamp(t, 0.0, u_tMax);
|
|
3001
|
+
t = t / max(u_tMax, 0.00001);
|
|
3002
|
+
|
|
3003
|
+
gl_FragColor = getGradientColor(t);
|
|
3004
|
+
}
|
|
3005
|
+
`);
|
|
3006
|
+
gl.useProgram(program);
|
|
3007
|
+
const positionBuffer = gl.createBuffer();
|
|
3008
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
3009
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
3010
|
+
-1,
|
|
3011
|
+
-1,
|
|
3012
|
+
1,
|
|
3013
|
+
-1,
|
|
3014
|
+
-1,
|
|
3015
|
+
1,
|
|
3016
|
+
-1,
|
|
3017
|
+
1,
|
|
3018
|
+
1,
|
|
3019
|
+
-1,
|
|
3020
|
+
1,
|
|
3021
|
+
1
|
|
3022
|
+
]), gl.STATIC_DRAW);
|
|
3023
|
+
const positionLocation = gl.getAttribLocation(program, "a_position");
|
|
3024
|
+
gl.enableVertexAttribArray(positionLocation);
|
|
3025
|
+
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
3026
|
+
const center = resolveRadialCenter(gradient.config.position, width, height);
|
|
3027
|
+
const radius = resolveRadialRadii(gradient.config.size, gradient.config.shape, center, width, height);
|
|
3028
|
+
const maxVisibleT = getMaxVisibleRadialT(center, radius, width, height);
|
|
3029
|
+
const baseStops = resolveRenderableGradientStops(gradient, getWebGLSampleCount(gradient, MAX_STOPS));
|
|
3030
|
+
const repeatMaxT = Math.min(maxVisibleT, MAX_REPEATING_RADIAL_T);
|
|
3031
|
+
const maxT = gradient.isRepeating ? repeatMaxT : 1;
|
|
3032
|
+
const renderStops = gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, repeatMaxT) : baseStops;
|
|
3033
|
+
const limitedStops = fitStopsToWebGLLimit(gradient.isRepeating ? normalizeStops(renderStops, 0, repeatMaxT) : renderStops, MAX_STOPS);
|
|
3034
|
+
const positions = new Float32Array(MAX_STOPS);
|
|
3035
|
+
const colors = new Float32Array(MAX_STOPS * 4);
|
|
3036
|
+
limitedStops.forEach((stop, index) => {
|
|
3037
|
+
const color = toWebGLColor(stop.value);
|
|
3038
|
+
positions[index] = stop.position;
|
|
3039
|
+
colors[index * 4 + 0] = color[0];
|
|
3040
|
+
colors[index * 4 + 1] = color[1];
|
|
3041
|
+
colors[index * 4 + 2] = color[2];
|
|
3042
|
+
colors[index * 4 + 3] = color[3];
|
|
3043
|
+
});
|
|
3044
|
+
gl.uniform2f(gl.getUniformLocation(program, "u_center"), center.x / width, 1 - center.y / height);
|
|
3045
|
+
gl.uniform2f(gl.getUniformLocation(program, "u_radius"), radius.x / width, radius.y / height);
|
|
3046
|
+
gl.uniform1f(gl.getUniformLocation(program, "u_tMax"), maxT);
|
|
3047
|
+
gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
|
|
3048
|
+
gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
|
|
3049
|
+
gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
|
|
3050
|
+
gl.clearColor(0, 0, 0, 0);
|
|
3051
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
3052
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
3053
|
+
} };
|
|
3054
|
+
}
|
|
3055
|
+
};
|
|
3056
|
+
//#endregion
|
|
2105
3057
|
//#region src/gradient-transformer/GradientTransformer.ts
|
|
2106
3058
|
var GradientTransformer = class {
|
|
2107
3059
|
static _modules = /* @__PURE__ */ new Map();
|
|
@@ -2139,6 +3091,8 @@ var GradientTransformer = class {
|
|
|
2139
3091
|
this.add(new ModuleTransformerRadialGradientToCanvas());
|
|
2140
3092
|
this.add(new ModuleTransformerConicGradientToCanvas());
|
|
2141
3093
|
this.add(new ModuleTransformerLinearGradientToCanvasWebGL());
|
|
3094
|
+
this.add(new ModuleTransformerRadialGradientToCanvasWebGL());
|
|
3095
|
+
this.add(new ModuleTransformerConicGradientToCanvasWebGL());
|
|
2142
3096
|
}
|
|
2143
3097
|
static _getKey(target, gradientType) {
|
|
2144
3098
|
return `${target}:${gradientType}`;
|
|
@@ -2200,4 +3154,4 @@ function transformFrom(target, gradientType, input) {
|
|
|
2200
3154
|
return GradientTransformer.from(target, gradientType, input);
|
|
2201
3155
|
}
|
|
2202
3156
|
//#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 };
|
|
3157
|
+
export { ConicGradient, GRADIENT_COLOR_SPACE, GRADIENT_HUE_INTERPOLATIONS, GRADIENT_POLAR_COLOR_SPACES, GradientBase, GradientFactory, GradientTransformer, LinearGradient, ModuleTransformerConicGradientToCanvas, ModuleTransformerConicGradientToCanvasWebGL, 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 };
|