UNPKG

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
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