Skip to content

Commit

Permalink
fix: Fix of APNG image decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor-pelykh committed May 8, 2024
1 parent 9e32ba7 commit 79f22e0
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 24 deletions.
32 changes: 18 additions & 14 deletions src/draw/draw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export interface FillRectOptions {
rect: Rectangle;
color: Color;
radius?: number;
alphaBlend?: boolean;
mask?: MemoryImage;
maskChannel?: Channel;
}
Expand Down Expand Up @@ -2000,9 +2001,10 @@ export abstract class Draw {
*/
public static fillRect(opt: FillRectOptions): MemoryImage {
const radius = opt.radius ?? 0;
const alphaBlend = opt.alphaBlend ?? true;
const maskChannel = opt.maskChannel ?? Channel.luminance;

if (opt.color.a === 0) {
if (alphaBlend && opt.color.a === 0) {
return opt.image;
}

Expand Down Expand Up @@ -2070,7 +2072,10 @@ export abstract class Draw {
}

// If no blending is necessary, use a faster fill method.
if (opt.color.a === opt.color.maxChannelValue && opt.mask === undefined) {
if (
!alphaBlend ||
(opt.color.a === opt.color.maxChannelValue && opt.mask === undefined)
) {
const range = opt.image.getRange(xx0, yy0, ww, hh);
let it: IteratorResult<Pixel> | undefined = undefined;
while (((it = range.next()), !it.done)) {
Expand Down Expand Up @@ -2193,37 +2198,36 @@ export abstract class Draw {
* if **center** is true, the **src** will be centered in **dst**.
*/
public static compositeImage(opt: CompositeImageOptions): MemoryImage {
let dst = opt.dst;
let dstX = opt.dstX ?? 0;
let dstY = opt.dstY ?? 0;
const srcX = opt.srcX ?? 0;
const srcY = opt.srcY ?? 0;
const srcW = opt.srcW ?? opt.src.width;
const srcH = opt.srcH ?? opt.src.height;
const dstW =
opt.dstW ??
(opt.dst.width < opt.src.width ? opt.dst.width : opt.src.width);
opt.dstW ?? (dst.width < opt.src.width ? dst.width : opt.src.width);
const dstH =
opt.dstH ??
(opt.dst.height < opt.src.height ? opt.dst.height : opt.src.height);
opt.dstH ?? (dst.height < opt.src.height ? dst.height : opt.src.height);
const blend = opt.blend ?? BlendMode.alpha;
const linearBlend = opt.linearBlend ?? false;
const center = opt.center ?? false;
const maskChannel = opt.maskChannel ?? Channel.luminance;

if (center) {
// if [src] is wider than [dst]
let wdt = opt.dst.width - opt.src.width;
let wdt = dst.width - opt.src.width;
if (wdt < 0) wdt = 0;
dstX = Math.trunc(wdt / 2);
// if [src] is higher than [dst]
let height = opt.dst.height - opt.src.height;
let height = dst.height - opt.src.height;
if (height < 0) height = 0;
dstY = Math.trunc(height / 2);
}

if (opt.dst.hasPalette) {
opt.dst.convert({
numChannels: opt.dst.numChannels,
if (dst.hasPalette) {
dst = dst.convert({
numChannels: dst.numChannels,
});
}

Expand All @@ -2241,7 +2245,7 @@ export abstract class Draw {
if (blend === BlendMode.direct) {
Draw.imgDirectComposite(
opt.src,
opt.dst,
dst,
dstX,
dstY,
dstW,
Expand All @@ -2254,7 +2258,7 @@ export abstract class Draw {
} else {
Draw.imgComposite(
opt.src,
opt.dst,
dst,
dstX,
dstY,
dstW,
Expand All @@ -2268,6 +2272,6 @@ export abstract class Draw {
);
}

return opt.dst;
return dst;
}
}
46 changes: 36 additions & 10 deletions src/formats/png-decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { BlendMode } from '../draw/blend-mode';
import { PngFilterType } from './png/png-filter-type';
import { Pixel } from '../image/pixel';
import { ImageFormat } from './image-format';
import { Rectangle } from '../common/rectangle';

/**
* Decode a PNG encoded image.
Expand Down Expand Up @@ -932,13 +933,17 @@ export class PngDecoder implements Decoder {
}

if (firstImage === undefined || lastImage === undefined) {
firstImage = image;
lastImage = image;
firstImage = image.convert({
numChannels: image.numChannels,
});
lastImage = firstImage;
// Convert to MS
lastImage.frameDuration = Math.trunc(frame.delay * 1000);
continue;
}

const prevFrame = this._info.frames[i - 1];

if (
image.width === lastImage.width &&
image.height === lastImage.height &&
Expand All @@ -953,20 +958,41 @@ export class PngDecoder implements Decoder {
continue;
}

const dispose = frame.dispose;
lastImage = MemoryImage.from(firstImage.getFrame(i - 1));

const dispose = prevFrame.dispose;
if (dispose === PngDisposeMode.background) {
lastImage = MemoryImage.from(lastImage);
lastImage.clear(this._info.backgroundColor);
} else if (dispose === PngDisposeMode.previous) {
lastImage = MemoryImage.from(lastImage);
} else {
lastImage = MemoryImage.from(lastImage);
Draw.fillRect({
image: lastImage,
rect: new Rectangle(
prevFrame.xOffset,
prevFrame.yOffset,
prevFrame.xOffset + prevFrame.width - 1,
prevFrame.yOffset + prevFrame.height - 1
),
color: this._info.backgroundColor ?? new ColorRgba8(0, 0, 0, 0),
alphaBlend: false,
});
} else if (dispose === PngDisposeMode.previous && i > 1) {
const prevImage = firstImage.getFrame(i - 2);
lastImage = Draw.compositeImage({
dst: lastImage,
src: prevImage,
dstX: prevFrame.xOffset,
dstY: prevFrame.yOffset,
dstW: prevFrame.width,
dstH: prevFrame.height,
srcX: prevFrame.xOffset,
srcY: prevFrame.yOffset,
srcW: prevFrame.width,
srcH: prevFrame.height,
});
}

// Convert to MS
lastImage.frameDuration = Math.trunc(frame.delay * 1000);

Draw.compositeImage({
lastImage = Draw.compositeImage({
dst: lastImage,
src: image,
dstX: frame.xOffset,
Expand Down

0 comments on commit 79f22e0

Please sign in to comment.