image-in-browser
Version:
Package for encoding / decoding images, transforming images, applying filters, drawing primitives on images on the client side (no need for server Node.js)
1,103 lines • 45.5 kB
JavaScript
import { LibError } from '../error/lib-error.js';
import { Point } from '../common/point.js';
import { Interpolation } from '../common/interpolation.js';
import { MathUtils } from '../common/math-utils.js';
import { ExifData } from '../exif/exif-data.js';
import { MemoryImage } from '../image/image.js';
import { ImageUtils } from '../image/image-utils.js';
import { FlipDirection } from './flip-direction.js';
import { TrimSide } from './trim-side.js';
import { Rectangle } from '../common/rectangle.js';
import { Draw } from '../draw/draw.js';
import { BlendMode } from '../draw/blend-mode.js';
import { TrimMode } from './trim-mode.js';
import { ExpandCanvasPosition } from './expand-canvas-position.js';
export class Transform {
static rotate90(src) {
var _a;
let firstFrame = undefined;
for (const frame of src.frames) {
const dst = (_a = firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame()) !== null && _a !== void 0 ? _a : MemoryImage.fromResized(frame, frame.height, frame.width, true);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
const hm1 = frame.height - 1;
for (let y = 0; y < dst.height; ++y) {
for (let x = 0; x < dst.width; ++x) {
dst.setPixel(x, y, frame.getPixel(y, hm1 - x));
}
}
}
return firstFrame;
}
static rotate180(src) {
var _a;
let firstFrame = undefined;
for (const frame of src.frames) {
const wm1 = frame.width - 1;
const hm1 = frame.height - 1;
const dst = (_a = firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame()) !== null && _a !== void 0 ? _a : MemoryImage.from(frame, true, true);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
for (let y = 0; y < dst.height; ++y) {
for (let x = 0; x < dst.width; ++x) {
dst.setPixel(x, y, frame.getPixel(wm1 - x, hm1 - y));
}
}
}
return firstFrame;
}
static rotate270(src) {
var _a;
let firstFrame = undefined;
for (const frame of src.frames) {
const wm1 = src.width - 1;
const dst = (_a = firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame()) !== null && _a !== void 0 ? _a : MemoryImage.fromResized(frame, frame.height, frame.width, true);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
for (let y = 0; y < dst.height; ++y) {
for (let x = 0; x < dst.width; ++x) {
dst.setPixel(x, y, frame.getPixel(wm1 - y, x));
}
}
}
return firstFrame;
}
static findTrim(opt) {
var _a, _b;
const mode = (_a = opt.mode) !== null && _a !== void 0 ? _a : TrimMode.transparent;
const sides = (_b = opt.sides) !== null && _b !== void 0 ? _b : TrimSide.all;
let h = opt.image.height;
let w = opt.image.width;
const bg = mode === TrimMode.topLeftColor
? opt.image.getPixel(0, 0)
: mode === TrimMode.bottomRightColor
? opt.image.getPixel(w - 1, h - 1)
: undefined;
let xMin = w;
let xMax = 0;
let yMin = undefined;
let yMax = 0;
for (let y = 0; y < h; ++y) {
let first = true;
for (let x = 0; x < w; ++x) {
const c = opt.image.getPixel(x, y);
if ((mode === TrimMode.transparent && c.a !== 0) ||
(mode !== TrimMode.transparent && (bg === undefined || !c.equals(bg)))) {
if (xMin > x) {
xMin = x;
}
if (xMax < x) {
xMax = x;
}
yMin !== null && yMin !== void 0 ? yMin : (yMin = y);
yMax = y;
if (first) {
x = xMax;
first = false;
}
}
}
}
if (yMin === undefined) {
return new Rectangle(0, 0, w, h);
}
if (!(sides & TrimSide.top)) {
yMin = 0;
}
if (!(sides & TrimSide.bottom)) {
yMax = h - 1;
}
if (!(sides & TrimSide.left)) {
xMin = 0;
}
if (!(sides & TrimSide.right)) {
xMax = w - 1;
}
w = 1 + xMax - xMin;
h = 1 + yMax - yMin;
return Rectangle.fromXYWH(xMin, yMin, w, h);
}
static bakeOrientation(opt) {
const bakedImage = MemoryImage.from(opt.image);
if (!opt.image.exifData.imageIfd.hasOrientation ||
opt.image.exifData.imageIfd.orientation === 1) {
return bakedImage;
}
bakedImage.exifData = ExifData.from(opt.image.exifData);
bakedImage.exifData.imageIfd.orientation = undefined;
switch (opt.image.exifData.imageIfd.orientation) {
case 2:
return Transform.flipHorizontal({
image: bakedImage,
});
case 3:
return Transform.flip({
image: bakedImage,
direction: FlipDirection.both,
});
case 4: {
const rotated = Transform.copyRotate({
image: bakedImage,
angle: 180,
});
return Transform.flipHorizontal({
image: rotated,
});
}
case 5: {
const rotated = Transform.copyRotate({
image: bakedImage,
angle: 90,
});
return Transform.flipHorizontal({
image: rotated,
});
}
case 6:
return Transform.copyRotate({
image: bakedImage,
angle: 90,
});
case 7: {
const rotated = Transform.copyRotate({
image: bakedImage,
angle: -90,
});
return Transform.flipHorizontal({
image: rotated,
});
}
case 8:
return Transform.copyRotate({
image: bakedImage,
angle: -90,
});
}
return bakedImage;
}
static copyCrop(opt) {
var _a, _b, _c;
let image = opt.image;
const radius = (_a = opt.radius) !== null && _a !== void 0 ? _a : 0;
const antialias = (_b = opt.antialias) !== null && _b !== void 0 ? _b : true;
const x = MathUtils.clampInt(opt.rect.left, 0, image.width - 1);
const y = MathUtils.clampInt(opt.rect.top, 0, image.height - 1);
const width = x + opt.rect.width > image.width ? image.width - x : opt.rect.width;
const height = y + opt.rect.height > image.height ? image.height - y : opt.rect.height;
if (radius > 0 && image.hasPalette) {
image = image.convert({
numChannels: image.numChannels,
});
}
let firstFrame = undefined;
const numFrames = image.numFrames;
for (let i = 0; i < numFrames; ++i) {
const frame = image.frames[i];
const dst = (_c = firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame()) !== null && _c !== void 0 ? _c : MemoryImage.fromResized(frame, width, height, true);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
if (radius > 0) {
const rad = Math.round(radius);
const rad2 = rad * rad;
const x1 = x;
const y1 = y;
const x2 = x + width;
const y2 = y + height;
const c1x = x1 + rad - 1;
const c1y = y1 + rad - 1;
const c2x = x2 - rad + 1;
const c2y = y1 + rad - 1;
const c3x = x2 - rad + 1;
const c3y = y2 - rad + 1;
const c4x = x1 + rad - 1;
const c4y = y2 - rad + 1;
const iter = frame.getRange(x1, y1, width, height);
let iterRes = undefined;
while (((iterRes = iter.next()), !iterRes.done)) {
const p = iterRes.value;
const px = p.x;
const py = p.y;
let a = 1;
if (px < c1x && py < c1y) {
a = ImageUtils.circleTest(p, new Point(c1x, c1y), rad2, antialias);
if (a === 0) {
dst.setPixelRgba(p.x - x1, p.y - y1, 0, 0, 0, 0);
continue;
}
}
else if (px > c2x && py < c2y) {
a = ImageUtils.circleTest(p, new Point(c2x, c2y), rad2, antialias);
if (a === 0) {
dst.setPixelRgba(p.x - x1, p.y - y1, 0, 0, 0, 0);
continue;
}
}
else if (px > c3x && py > c3y) {
a = ImageUtils.circleTest(p, new Point(c3x, c3y), rad2, antialias);
if (a === 0) {
dst.setPixelRgba(p.x - x1, p.y - y1, 0, 0, 0, 0);
continue;
}
}
else if (px < c4x && py > c4y) {
a = ImageUtils.circleTest(p, new Point(c4x, c4y), rad2, antialias);
if (a === 0) {
dst.setPixelRgba(p.x - x1, p.y - y1, 0, 0, 0, 0);
continue;
}
}
if (a !== 1) {
dst.getPixel(p.x - x1, p.y - y1).setRgba(p.r, p.g, p.b, p.a * a);
}
else {
dst.setPixel(p.x - x1, p.y - y1, p);
}
}
}
else {
for (const p of dst) {
p.set(frame.getPixel(x + p.x, y + p.y));
}
}
}
return firstFrame;
}
static copyCropCircle(opt) {
var _a, _b, _c, _d, _e, _f, _g, _h;
let image = opt.image;
let centerX = (_b = (_a = opt.center) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : Math.trunc(image.width / 2);
let centerY = (_d = (_c = opt.center) === null || _c === void 0 ? void 0 : _c.y) !== null && _d !== void 0 ? _d : Math.trunc(image.height / 2);
let radius = (_e = opt.radius) !== null && _e !== void 0 ? _e : Math.trunc(Math.min(image.width, image.height) / 2);
const antialias = (_f = opt.antialias) !== null && _f !== void 0 ? _f : true;
centerX = MathUtils.clamp(centerX, 0, image.width - 1);
centerY = MathUtils.clamp(centerY, 0, image.height - 1);
if (radius < 1) {
radius = Math.trunc(Math.min(image.width, image.height) / 2);
}
const tlx = centerX - radius;
const tly = centerY - radius;
const wh = radius * 2;
const radiusSqr = radius * radius;
if (image.hasPalette) {
image = image.convert({
numChannels: 4,
});
}
let firstFrame = undefined;
const numFrames = image.numFrames;
for (let i = 0; i < numFrames; ++i) {
const frame = image.frames[i];
const dst = (_g = firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame()) !== null && _g !== void 0 ? _g : MemoryImage.fromResized(frame, wh, wh, true);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
const bg = (_h = frame.backgroundColor) !== null && _h !== void 0 ? _h : image.backgroundColor;
if (bg !== undefined) {
dst.clear(bg);
}
const dh = dst.height;
const dw = radius * 2;
for (let yi = 0, sy = tly; yi < dh; ++yi, ++sy) {
for (let xi = 0, sx = tlx; xi < dw; ++xi, ++sx) {
const p = frame.getPixel(sx, sy);
const a = ImageUtils.circleTest(p, new Point(centerX, centerY), radiusSqr, antialias);
if (a !== 1) {
dst.getPixel(xi, yi).setRgba(p.r, p.g, p.b, p.a * a);
}
else {
dst.setPixel(xi, yi, p);
}
}
}
}
return firstFrame;
}
static copyExpandCanvas(opt) {
var _a, _b;
const position = (_a = opt.position) !== null && _a !== void 0 ? _a : ExpandCanvasPosition.center;
let newWidth = opt.newWidth;
let newHeight = opt.newHeight;
const padding = opt.padding;
if ((newWidth === undefined || newHeight === undefined) &&
padding === undefined) {
throw new LibError('Either new dimensions or padding must be provided.');
}
else if (newWidth !== undefined &&
newHeight !== undefined &&
padding !== undefined) {
throw new LibError('Cannot provide both new dimensions and padding.');
}
if (padding !== undefined) {
newWidth = opt.image.width + padding * 2;
newHeight = opt.image.height + padding * 2;
}
const srcConverted = opt.image.hasPalette
? opt.image.convert({
numChannels: opt.image.numChannels,
})
: opt.image;
if (newWidth < srcConverted.width || newHeight < srcConverted.height) {
throw new LibError('New dimensions must be larger or equal to the original image.');
}
if (opt.toImage !== undefined &&
(opt.toImage.width !== newWidth || opt.toImage.height !== newHeight)) {
throw new LibError('Provided image does not match the new dimensions.');
}
const expandedCanvas = (_b = opt.toImage) !== null && _b !== void 0 ? _b : new MemoryImage({
width: newWidth,
height: newHeight,
format: opt.image.format,
});
if (opt.backgroundColor !== undefined) {
expandedCanvas.clear(opt.backgroundColor);
}
let xPos = 0;
let yPos = 0;
switch (position) {
case ExpandCanvasPosition.topLeft:
xPos = 0;
yPos = 0;
break;
case ExpandCanvasPosition.topCenter:
xPos = Math.trunc((newWidth - srcConverted.width) / 2);
yPos = 0;
break;
case ExpandCanvasPosition.topRight:
xPos = newWidth - srcConverted.width;
yPos = 0;
break;
case ExpandCanvasPosition.centerLeft:
xPos = 0;
yPos = Math.trunc((newHeight - srcConverted.height) / 2);
break;
case ExpandCanvasPosition.center:
xPos = Math.trunc((newWidth - srcConverted.width) / 2);
yPos = Math.trunc((newHeight - srcConverted.height) / 2);
break;
case ExpandCanvasPosition.centerRight:
xPos = newWidth - srcConverted.width;
yPos = Math.trunc((newHeight - srcConverted.height) / 2);
break;
case ExpandCanvasPosition.bottomLeft:
xPos = 0;
yPos = newHeight - srcConverted.height;
break;
case ExpandCanvasPosition.bottomCenter:
xPos = Math.trunc((newWidth - srcConverted.width) / 2);
yPos = newHeight - srcConverted.height;
break;
case ExpandCanvasPosition.bottomRight:
xPos = newWidth - srcConverted.width;
yPos = newHeight - srcConverted.height;
break;
default:
throw new LibError('Invalid position provided.');
}
for (let i = 0; i < srcConverted.numFrames; ++i) {
if (i >= expandedCanvas.numFrames) {
expandedCanvas.addFrame();
}
const frame = srcConverted.frames[i];
const expandedCanvasFrame = expandedCanvas.frames[i];
for (const p of frame) {
if (xPos + p.x >= newWidth || yPos + p.y >= newHeight) {
continue;
}
if (p.a === p.maxChannelValue) {
expandedCanvasFrame.setPixel(xPos + p.x, yPos + p.y, p);
}
else {
Draw.drawPixel({
image: expandedCanvasFrame,
pos: new Point(xPos + p.x, yPos + p.y),
color: p,
});
}
}
}
return expandedCanvas;
}
static copyFlip(opt) {
return Transform.flip({
image: opt.image.clone(),
direction: opt.direction,
});
}
static copyRectify(opt) {
var _a, _b, _c;
const interpolation = (_a = opt.interpolation) !== null && _a !== void 0 ? _a : Interpolation.nearest;
const src = interpolation !== Interpolation.nearest && opt.image.hasPalette
? opt.image.convert({
numChannels: opt.image.numChannels,
})
: opt.image;
let firstFrame = undefined;
for (const frame of src.frames) {
const dst = (_c = (_b = firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame()) !== null && _b !== void 0 ? _b : opt.toImage) !== null && _c !== void 0 ? _c : MemoryImage.from(frame, true);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
for (let y = 0; y < dst.height; ++y) {
const v = y / (dst.height - 1);
for (let x = 0; x < dst.width; ++x) {
const u = x / (dst.width - 1);
const srcPixelCoord = opt.topLeft
.mul((1 - u) * (1 - v))
.add(opt.topRight
.mul(u * (1 - v))
.add(opt.bottomLeft
.mul((1 - u) * v)
.add(opt.bottomRight.mul(u * v))));
const srcPixel = interpolation === Interpolation.nearest
? frame.getPixel(Math.trunc(srcPixelCoord.x), Math.trunc(srcPixelCoord.y))
: frame.getPixelInterpolate(srcPixelCoord.x, srcPixelCoord.y, interpolation);
dst.setPixel(x, y, srcPixel);
}
}
}
return firstFrame;
}
static copyResize(opt) {
var _a, _b;
let src = opt.image;
let interpolation = (_a = opt.interpolation) !== null && _a !== void 0 ? _a : Interpolation.nearest;
let maintainAspect = (_b = opt.maintainAspect) !== null && _b !== void 0 ? _b : false;
if (opt.width === undefined && opt.height === undefined) {
throw new LibError('Invalid size. Please specify the width or height.');
}
if (src.hasPalette) {
interpolation = Interpolation.nearest;
}
if (src.exifData.imageIfd.hasOrientation &&
src.exifData.imageIfd.orientation !== 1) {
src = Transform.bakeOrientation({
image: src,
});
}
let x1 = 0;
let y1 = 0;
let x2 = 0;
let y2 = 0;
if (opt.width !== undefined &&
opt.height !== undefined &&
maintainAspect === true) {
x1 = 0;
x2 = opt.width;
const srcAspect = src.height / src.width;
const h = Math.trunc(opt.width * srcAspect);
const dy = Math.trunc((opt.height - h) / 2);
y1 = dy;
y2 = y1 + h;
if (y1 < 0 || y2 > opt.height) {
y1 = 0;
y2 = opt.height;
const srcAspect = src.width / src.height;
const w = Math.trunc(opt.height * srcAspect);
const dx = Math.trunc((opt.width - w) / 2);
x1 = dx;
x2 = x1 + w;
}
}
else {
maintainAspect = false;
}
const height = opt.height === undefined || opt.height <= 0
? Math.round(opt.width * (src.height / src.width))
: opt.height;
const width = opt.width === undefined || opt.width <= 0
? Math.round(opt.height * (src.width / src.height))
: opt.width;
const w = maintainAspect ? x2 - x1 : width;
const h = maintainAspect ? y2 - y1 : height;
if (!maintainAspect) {
x1 = 0;
x2 = width;
y1 = 0;
y2 = height;
}
if (width === src.width && height === src.height) {
return src.clone();
}
const scaleX = new Int32Array(w);
const dx = src.width / w;
for (let x = 0; x < w; ++x) {
scaleX[x] = Math.trunc(x * dx);
}
let firstFrame = undefined;
const numFrames = src.numFrames;
for (let i = 0; i < numFrames; ++i) {
const frame = src.frames[i];
const dst = MemoryImage.fromResized(frame, width, height, true);
firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame(dst);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
const dy = frame.height / h;
const dx = frame.width / w;
if (maintainAspect && opt.backgroundColor !== undefined) {
dst.clear(opt.backgroundColor);
}
if (interpolation === Interpolation.average) {
for (let y = 0; y < h; ++y) {
const ay1 = Math.trunc(y * dy);
let ay2 = Math.trunc((y + 1) * dy);
if (ay2 === ay1) {
ay2++;
}
for (let x = 0; x < w; ++x) {
const ax1 = Math.trunc(x * dx);
let ax2 = Math.trunc((x + 1) * dx);
if (ax2 === ax1) {
ax2++;
}
let r = 0;
let g = 0;
let b = 0;
let a = 0;
let np = 0;
for (let sy = ay1; sy < ay2; ++sy) {
for (let sx = ax1; sx < ax2; ++sx, ++np) {
const s = frame.getPixel(sx, sy);
r += s.r;
g += s.g;
b += s.b;
a += s.a;
}
}
dst.setPixel(x1 + x, y1 + y, dst.getColor(r / np, g / np, b / np, a / np));
}
}
}
else if (interpolation === Interpolation.nearest) {
if (frame.hasPalette) {
for (let y = 0; y < h; ++y) {
const y2 = Math.trunc(y * dy);
for (let x = 0; x < w; ++x) {
dst.setPixelIndex(x1 + x, y1 + y, frame.getPixelIndex(scaleX[x], y2));
}
}
}
else {
for (let y = 0; y < h; ++y) {
const y2 = Math.trunc(y * dy);
for (let x = 0; x < w; ++x) {
dst.setPixel(x1 + x, y1 + y, frame.getPixel(scaleX[x], y2));
}
}
}
}
else {
for (let y = 0; y < h; ++y) {
const sy2 = y * dy;
for (let x = 0; x < w; ++x) {
const sx2 = x * dx;
dst.setPixel(x, y, frame.getPixelInterpolate(x1 + sx2, y1 + sy2, interpolation));
}
}
}
}
return firstFrame;
}
static copyResizeCropSquare(opt) {
var _a, _b, _c, _d;
const src = opt.image;
let interpolation = (_a = opt.interpolation) !== null && _a !== void 0 ? _a : Interpolation.nearest;
const radius = (_b = opt.radius) !== null && _b !== void 0 ? _b : 0;
const antialias = (_c = opt.antialias) !== null && _c !== void 0 ? _c : true;
if (opt.size <= 0) {
throw new LibError('Invalid size.');
}
if (src.hasPalette) {
interpolation = Interpolation.nearest;
}
let height = opt.size;
let width = opt.size;
if (src.width < src.height) {
height = Math.trunc(opt.size * (src.height / src.width));
}
else if (src.width > src.height) {
width = Math.trunc(opt.size * (src.width / src.height));
}
const dy = src.height / height;
const dx = src.width / width;
const xOffset = Math.trunc((width - opt.size) / 2);
const yOffset = Math.trunc((height - opt.size) / 2);
const scaleX = interpolation === Interpolation.nearest
? new Int32Array(opt.size)
: undefined;
if (scaleX !== undefined) {
for (let x = 0; x < opt.size; ++x) {
scaleX[x] = Math.trunc((x + xOffset) * dx);
}
}
let firstFrame = undefined;
for (const frame of src.frames) {
const dst = (_d = firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame()) !== null && _d !== void 0 ? _d : MemoryImage.fromResized(frame, opt.size, opt.size, true);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
if (radius > 0) {
const rad = Math.round(radius);
const rad2 = rad * rad;
const x1 = 0;
const y1 = 0;
const x2 = opt.size - 1;
const y2 = opt.size - 1;
const c1x = x1 + rad - 1;
const c1y = y1 + rad - 1;
const c2x = x2 - rad + 1;
const c2y = y1 + rad - 1;
const c3x = x2 - rad + 1;
const c3y = y2 - rad + 1;
const c4x = x1 + rad - 1;
const c4y = y2 - rad + 1;
const iter = dst.getRange(x1, y1, width, height);
let iterRes = undefined;
while (((iterRes = iter.next()), !iterRes.done)) {
const p = iterRes.value;
const px = p.x;
const py = p.y;
let a = 1;
if (px < c1x && py < c1y) {
a = ImageUtils.circleTest(p, new Point(c1x, c1y), rad2, antialias);
if (a === 0) {
p.setRgba(0, 0, 0, 0);
continue;
}
}
else if (px > c2x && py < c2y) {
a = ImageUtils.circleTest(p, new Point(c2x, c2y), rad2, antialias);
if (a === 0) {
p.setRgba(0, 0, 0, 0);
continue;
}
}
else if (px > c3x && py > c3y) {
a = ImageUtils.circleTest(p, new Point(c3x, c3y), rad2, antialias);
if (a === 0) {
p.setRgba(0, 0, 0, 0);
continue;
}
}
else if (px < c4x && py > c4y) {
a = ImageUtils.circleTest(p, new Point(c4x, c4y), rad2, antialias);
if (a === 0) {
p.setRgba(0, 0, 0, 0);
continue;
}
}
if (interpolation === Interpolation.nearest) {
const sy = Math.trunc((p.y + yOffset) * dy);
const sp = frame.getPixel(scaleX[p.x], sy);
p.setRgba(sp.r, sp.g, sp.b, sp.a * a);
}
else {
const x = p.x * dx;
const y = p.y * dy;
const sp = frame.getPixelInterpolate(x, y, interpolation);
const spa = sp.a * a;
p.setRgba(sp.r, sp.g, sp.b, spa);
}
}
return dst;
}
if (interpolation === Interpolation.nearest) {
for (let y = 0; y < opt.size; ++y) {
const y2 = Math.trunc((y + yOffset) * dy);
for (let x = 0; x < opt.size; ++x) {
dst.setPixel(x, y, frame.getPixel(scaleX[x], y2));
}
}
}
else {
for (const p of dst) {
const x = p.x * dx;
const y = p.y * dy;
p.set(frame.getPixelInterpolate(x, y, interpolation));
}
}
}
return firstFrame;
}
static copyRotate(opt) {
var _a, _b, _c;
const src = opt.image;
let interpolation = (_a = opt.interpolation) !== null && _a !== void 0 ? _a : Interpolation.nearest;
const nAngle = opt.angle % 360;
if (src.hasPalette) {
interpolation = Interpolation.nearest;
}
if (nAngle % 90 === 0) {
const iAngle = Math.trunc(nAngle / 90);
switch (iAngle) {
case 1:
return Transform.rotate90(src);
case 2:
return Transform.rotate180(src);
case 3:
return Transform.rotate270(src);
default:
return MemoryImage.from(src);
}
}
const rad = (nAngle * Math.PI) / 180;
const ca = Math.cos(rad);
const sa = Math.sin(rad);
const ux = Math.abs(src.width * ca);
const uy = Math.abs(src.width * sa);
const vx = Math.abs(src.height * sa);
const vy = Math.abs(src.height * ca);
const w2 = 0.5 * src.width;
const h2 = 0.5 * src.height;
const dw2 = 0.5 * (ux + vx);
const dh2 = 0.5 * (uy + vy);
let firstFrame = undefined;
const numFrames = src.numFrames;
for (let i = 0; i < numFrames; ++i) {
const frame = src.frames[i];
const dst = (_b = firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame()) !== null && _b !== void 0 ? _b : MemoryImage.fromResized(src, Math.trunc(ux + vx), Math.trunc(uy + vy), true);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
const bg = (_c = frame.backgroundColor) !== null && _c !== void 0 ? _c : src.backgroundColor;
if (bg !== undefined) {
dst.clear(bg);
}
for (const p of dst) {
const x = p.x;
const y = p.y;
const x2 = w2 + (x - dw2) * ca + (y - dh2) * sa;
const y2 = h2 - (x - dw2) * sa + (y - dh2) * ca;
if (frame.isBoundsSafe(x2, y2)) {
const c = frame.getPixelInterpolate(x2, y2, interpolation);
dst.setPixel(x, y, c);
}
}
}
return firstFrame;
}
static flip(opt) {
switch (opt.direction) {
case FlipDirection.horizontal:
Transform.flipHorizontal(opt);
break;
case FlipDirection.vertical:
Transform.flipVertical(opt);
break;
case FlipDirection.both:
Transform.flipHorizontalVertical(opt);
break;
}
return opt.image;
}
static flipVertical(opt) {
const numFrames = opt.image.numFrames;
for (let i = 0; i < numFrames; ++i) {
const frame = opt.image.frames[i];
const w = frame.width;
const h = frame.height;
const h2 = Math.trunc(h / 2);
if (opt.image.hasPalette) {
for (let y = 0, y2 = h - 1; y < h2; ++y, --y2) {
for (let x = 0; x < w; ++x) {
const p1 = frame.getPixel(x, y);
const p2 = frame.getPixel(x, y2);
const t = p1.index;
p1.index = p2.index;
p2.index = t;
}
}
}
else {
for (let y = 0, y2 = h - 1; y < h2; ++y, --y2) {
for (let x = 0; x < w; ++x) {
const p1 = frame.getPixel(x, y);
const p2 = frame.getPixel(x, y2);
let t = p1.r;
p1.r = p2.r;
p2.r = t;
t = p1.g;
p1.g = p2.g;
p2.g = t;
t = p1.b;
p1.b = p2.b;
p2.b = t;
t = p1.a;
p1.a = p2.a;
p2.a = t;
}
}
}
}
return opt.image;
}
static flipHorizontal(opt) {
const numFrames = opt.image.numFrames;
for (let i = 0; i < numFrames; ++i) {
const frame = opt.image.frames[i];
const w = frame.width;
const h = frame.height;
const w2 = Math.trunc(w / 2);
if (opt.image.hasPalette) {
for (let y = 0; y < h; ++y) {
for (let x = 0, x2 = w - 1; x < w2; ++x, --x2) {
const p1 = frame.getPixel(x, y);
const p2 = frame.getPixel(x2, y);
const t = p1.index;
p1.index = p2.index;
p2.index = t;
}
}
}
else {
for (let y = 0; y < h; ++y) {
for (let x = 0, x2 = w - 1; x < w2; ++x, --x2) {
const p1 = frame.getPixel(x, y);
const p2 = frame.getPixel(x2, y);
let t = p1.r;
p1.r = p2.r;
p2.r = t;
t = p1.g;
p1.g = p2.g;
p2.g = t;
t = p1.b;
p1.b = p2.b;
p2.b = t;
t = p1.a;
p1.a = p2.a;
p2.a = t;
}
}
}
}
return opt.image;
}
static flipHorizontalVertical(opt) {
const numFrames = opt.image.numFrames;
for (let i = 0; i < numFrames; ++i) {
const frame = opt.image.frames[i];
const w = frame.width;
const h = frame.height;
const h2 = Math.trunc(h / 2);
if (frame.hasPalette) {
for (let y = 0, y2 = h - 1; y < h2; ++y, --y2) {
for (let x = 0, x2 = w - 1; x < w; ++x, --x2) {
const p1 = frame.getPixel(x, y);
const p2 = frame.getPixel(x2, y2);
const t = p1.index;
p1.index = p2.index;
p2.index = t;
}
}
}
else {
for (let y = 0, y2 = h - 1; y < h2; ++y, --y2) {
for (let x = 0, x2 = w - 1; x < w; ++x, --x2) {
const p1 = frame.getPixel(x, y);
const p2 = frame.getPixel(x2, y2);
let t = p1.r;
p1.r = p2.r;
p2.r = t;
t = p1.g;
p1.g = p2.g;
p2.g = t;
t = p1.b;
p1.b = p2.b;
p2.b = t;
t = p1.a;
p1.a = p2.a;
p2.a = t;
}
}
}
}
return opt.image;
}
static trim(opt) {
var _a, _b, _c;
const mode = (_a = opt.mode) !== null && _a !== void 0 ? _a : TrimMode.topLeftColor;
const sides = (_b = opt.sides) !== null && _b !== void 0 ? _b : TrimSide.all;
if (mode === TrimMode.transparent && opt.image.numChannels === 3) {
return MemoryImage.from(opt.image);
}
const crop = Transform.findTrim({
image: opt.image,
mode: mode,
sides: sides,
});
let firstFrame = undefined;
for (const frame of opt.image.frames) {
const dst = (_c = firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.addFrame()) !== null && _c !== void 0 ? _c : MemoryImage.fromResized(frame, crop.width, crop.height, true);
firstFrame !== null && firstFrame !== void 0 ? firstFrame : (firstFrame = dst);
Draw.compositeImage({
dst: dst,
src: opt.image,
srcX: crop.left,
srcY: crop.top,
srcW: crop.width,
srcH: crop.height,
blend: BlendMode.direct,
});
}
return firstFrame;
}
static resize(opt) {
var _a;
let src = opt.image;
let width = opt.width;
let height = opt.height;
let maintainAspect = opt.maintainAspect;
let interpolation = (_a = opt.interpolation) !== null && _a !== void 0 ? _a : Interpolation.nearest;
if (width === undefined && height === undefined) {
throw new LibError('Invalid size');
}
if (src.hasPalette) {
interpolation = Interpolation.nearest;
}
if (src.exifData.imageIfd.hasOrientation &&
src.exifData.imageIfd.orientation !== 1) {
src = Transform.bakeOrientation({
image: src,
});
}
let x1 = 0;
let y1 = 0;
let x2 = 0;
let y2 = 0;
if (width !== undefined &&
height !== undefined &&
maintainAspect === true) {
x1 = 0;
x2 = width;
const srcAspect = src.height / src.width;
const h = Math.trunc(width * srcAspect);
const dy = Math.trunc((height - h) / 2);
y1 = dy;
y2 = y1 + h;
if (y1 < 0 || y2 > height) {
y1 = 0;
y2 = height;
const srcAspect = src.width / src.height;
const w = Math.trunc(height * srcAspect);
const dx = Math.trunc((width - w) / 2);
x1 = dx;
x2 = x1 + w;
}
}
else {
maintainAspect = false;
}
if (height === undefined || height <= 0) {
height = Math.round(width * (src.height / src.width));
}
if (width === undefined || width <= 0) {
width = Math.round(height * (src.width / src.height));
}
const w = maintainAspect ? x2 - x1 : width;
const h = maintainAspect ? y2 - y1 : height;
if (!maintainAspect) {
x1 = 0;
x2 = width;
y1 = 0;
y2 = height;
}
if (width === src.width && height === src.height) {
return src;
}
if (width * height > src.width * src.height) {
return Transform.copyResize({
image: src,
width: width,
height: height,
maintainAspect: maintainAspect,
backgroundColor: opt.backgroundColor,
interpolation: interpolation,
});
}
const scaleX = new Int32Array(w);
const dx = src.width / w;
for (let x = 0; x < w; ++x) {
scaleX[x] = Math.trunc(x * dx);
}
const origWidth = src.width;
const origHeight = src.height;
const numFrames = src.numFrames;
for (let i = 0; i < numFrames; ++i) {
const frame = src.frames[i];
const dst = frame;
const dy = frame.height / h;
const dx = frame.width / w;
if (maintainAspect && opt.backgroundColor !== undefined) {
dst.clear(opt.backgroundColor);
}
if (interpolation === Interpolation.average) {
for (let y = 0; y < h; ++y) {
const ay1 = Math.trunc(y * dy);
let ay2 = Math.trunc((y + 1) * dy);
if (ay2 === ay1) {
ay2++;
}
for (let x = 0; x < w; ++x) {
const ax1 = Math.trunc(x * dx);
let ax2 = Math.trunc((x + 1) * dx);
if (ax2 === ax1) {
ax2++;
}
let r = 0;
let g = 0;
let b = 0;
let a = 0;
let np = 0;
for (let sy = ay1; sy < ay2; ++sy) {
for (let sx = ax1; sx < ax2; ++sx, ++np) {
const s = frame.getPixel(sx, sy);
r += s.r;
g += s.g;
b += s.b;
a += s.a;
}
}
const c = dst.getColor(r / np, g / np, b / np, a / np);
dst.data.width = width;
dst.data.height = height;
dst.setPixel(x1 + x, y1 + y, c);
dst.data.width = origWidth;
dst.data.height = origHeight;
}
}
}
else if (interpolation === Interpolation.nearest) {
if (frame.hasPalette) {
for (let y = 0; y < h; ++y) {
const y2 = Math.trunc(y * dy);
for (let x = 0; x < w; ++x) {
const p = frame.getPixelIndex(scaleX[x], y2);
dst.data.width = width;
dst.data.height = height;
dst.setPixelIndex(x1 + x, y1 + y, p);
dst.data.width = origWidth;
dst.data.height = origHeight;
}
}
}
else {
for (let y = 0; y < h; ++y) {
const y2 = Math.trunc(y * dy);
for (let x = 0; x < w; ++x) {
const p = frame.getPixel(scaleX[x], y2);
dst.data.width = width;
dst.data.height = height;
dst.setPixel(x1 + x, y1 + y, p);
dst.data.width = origWidth;
dst.data.height = origHeight;
}
}
}
}
else {
for (let y = 0; y < h; ++y) {
const sy2 = y * dy;
for (let x = 0; x < w; ++x) {
const sx2 = x * dx;
const p = frame.getPixelInterpolate(x1 + sx2, y1 + sy2, interpolation);
dst.data.width = width;
dst.data.height = height;
dst.setPixel(x, y, p);
dst.data.width = origWidth;
dst.data.height = origHeight;
}
}
}
dst.data.width = width;
dst.data.height = height;
}
return src;
}
}
//# sourceMappingURL=transform.js.map