Skip to content

Commit 7934642

Browse files
committed
multi-point gradients
(cherry picked from commit 0b07810)
1 parent b9cbecb commit 7934642

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

‎ImageScript.js‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,80 @@ export class Image {
948948
return this;
949949
}
950950

951+
/**
952+
* Creates a multi-point gradient generator
953+
* @param {Object<number, number>} colors The gradient points to use (e.g. `{0: 0xff0000ff, 1: 0x00ff00ff}`)
954+
* @return {(function(number): number)} The gradient generator. The function argument is the position in the gradient (0..1).
955+
*/
956+
static gradient(colors) {
957+
const entries = Object.entries(colors).sort((a, b) => a[0] - b[0]);
958+
const positions = entries.map(e => parseFloat(e[0]));
959+
const values = entries.map(e => e[1]);
960+
961+
if (positions.length === 0) throw new RangeError('Invalid gradient point count');
962+
else if (positions.length === 1) {
963+
return () => values[0];
964+
} else if (positions.length === 2) {
965+
const gradient = this.__gradient__(values[0], values[1]);
966+
return position => {
967+
if (position <= positions[0]) return values[0];
968+
if (position >= positions[1]) return values[1];
969+
return gradient((position - positions[0]) / (positions[1] - positions[0]));
970+
};
971+
}
972+
973+
const minDef = Math.min(...positions);
974+
const maxDef = Math.max(...positions);
975+
let gradients = [];
976+
977+
for (let i = 0; i < positions.length; i++) {
978+
let minPos = positions[i - 1];
979+
if (minPos === undefined) continue;
980+
981+
let maxPos = positions[i];
982+
983+
let minVal = values[i - 1];
984+
if (minVal === undefined) minVal = values[i];
985+
986+
const maxVal = values[i];
987+
const gradient = this.__gradient__(minVal, maxVal);
988+
989+
gradients.push({min: minPos, max: maxPos, gradient});
990+
}
991+
992+
return position => {
993+
if (position <= minDef) return gradients[0].gradient(0);
994+
if (position >= maxDef) return gradients[gradients.length - 1].gradient(1);
995+
996+
for (const gradient of gradients)
997+
if (position >= gradient.min && position <= gradient.max)
998+
return gradient.gradient((position - gradient.min) / (gradient.max - gradient.min));
999+
throw new RangeError(`Invalid gradient position: ${position}`);
1000+
};
1001+
}
1002+
1003+
/**
1004+
* @private
1005+
*/
1006+
static __gradient__(startColor, endColor) {
1007+
const sr = startColor >>> 24;
1008+
const sg = startColor >> 16 & 0xff;
1009+
const sb = startColor >> 8 & 0xff;
1010+
const sa = startColor & 0xff;
1011+
const er = (endColor >>> 24) - sr;
1012+
const eg = (endColor >> 16 & 0xff) - sg;
1013+
const eb = (endColor >> 8 & 0xff) - sb;
1014+
const ea = (endColor & 0xff) - sa;
1015+
1016+
return position => {
1017+
const r = sr + position * er;
1018+
const g = sg + position * eg;
1019+
const b = sb + position * eb;
1020+
const a = sa + position * ea;
1021+
return (((r & 0xff) << 24) | ((g & 0xff) << 16) | ((b & 0xff) << 8) | (a & 0xff));
1022+
};
1023+
}
1024+
9511025
/**
9521026
* Encodes the image into a PNG
9531027
* @param {number} compression The compression level to use (0-3)

0 commit comments

Comments
 (0)