Skip to content

Commit

Permalink
fix(blp): handle unusually sized mip data at very small sizes
Browse files Browse the repository at this point in the history
  • Loading branch information
fallenoak committed Jan 3, 2024
1 parent cd6d032 commit 5d83376
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 7 deletions.
32 changes: 27 additions & 5 deletions src/lib/blp/Blp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ import {
BLP_PIXEL_FORMAT,
MAX_MIP_LEVELS,
} from './const.js';
import { dxt1ToAbgr8888, dxt3ToAbgr8888, dxt5ToAbgr8888 } from './dxt.js';
import {
dxt1ToAbgr8888,
dxt3ToAbgr8888,
dxt5ToAbgr8888,
getDxt1Size,
getDxt3Size,
getDxt5Size,
} from './dxt.js';
import { palToAbgr8888 } from './pal.js';
import { rawAbgr8888ToArgb8888, rawArgb8888ToAbgr8888 } from './raw.js';
import * as blpIo from './io.js';
import { calcMipLevelCount, getSizeAtMipLevel, resizeBilinear } from './util.js';
import { calcMipLevelCount, getResizedBytes, getSizeAtMipLevel, resizeBilinear } from './util.js';
import BlpImage from './BlpImage.js';

class Blp {
Expand Down Expand Up @@ -316,7 +323,12 @@ class Blp {
#getDxt1Image(level: number, outputFormat: BLP_IMAGE_FORMAT): BlpImage {
const width = getSizeAtMipLevel(this.#width, level);
const height = getSizeAtMipLevel(this.#height, level);
const data = this.#images[level];

if (width === 0 || height === 0) {
return new BlpImage(width, height, new Uint8Array(0), outputFormat);
}

const data = getResizedBytes(this.#images[level], getDxt1Size(width, height));

switch (outputFormat) {
case BLP_IMAGE_FORMAT.IMAGE_DXT1:
Expand All @@ -333,7 +345,12 @@ class Blp {
#getDxt3Image(level: number, outputFormat: BLP_IMAGE_FORMAT): BlpImage {
const width = getSizeAtMipLevel(this.#width, level);
const height = getSizeAtMipLevel(this.#height, level);
const data = this.#images[level];

if (width === 0 || height === 0) {
return new BlpImage(width, height, new Uint8Array(0), outputFormat);
}

const data = getResizedBytes(this.#images[level], getDxt3Size(width, height));

switch (outputFormat) {
case BLP_IMAGE_FORMAT.IMAGE_DXT3:
Expand All @@ -350,7 +367,12 @@ class Blp {
#getDxt5Image(level: number, outputFormat: BLP_IMAGE_FORMAT) {
const width = getSizeAtMipLevel(this.#width, level);
const height = getSizeAtMipLevel(this.#height, level);
const data = this.#images[level];

if (width === 0 || height === 0) {
return new BlpImage(width, height, new Uint8Array(0), outputFormat);
}

const data = getResizedBytes(this.#images[level], getDxt5Size(width, height));

switch (outputFormat) {
case BLP_IMAGE_FORMAT.IMAGE_DXT5:
Expand Down
14 changes: 13 additions & 1 deletion src/lib/blp/dxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,18 @@ const dxtToAbgr8888 = (
return output8;
};

const getDxtSize = (width: number, height: number, blockSize: number) => {
const blockWidth = Math.ceil(width / DXT_BLOCK_WIDTH);
const blockHeight = Math.ceil(height / DXT_BLOCK_HEIGHT);
return blockWidth * blockHeight * blockSize;
};

const getDxt1Size = (width: number, height: number) => getDxtSize(width, height, DXT1_BLOCK_SIZE);

const getDxt3Size = (width: number, height: number) => getDxtSize(width, height, DXT3_BLOCK_SIZE);

const getDxt5Size = (width: number, height: number) => getDxtSize(width, height, DXT5_BLOCK_SIZE);

const dxt1ToAbgr8888 = (width: number, height: number, input: Uint8Array) =>
dxtToAbgr8888(width, height, input, DXT1_BLOCK_SIZE, dxt1DecompressBlock);

Expand All @@ -271,4 +283,4 @@ const dxt3ToAbgr8888 = (width: number, height: number, input: Uint8Array) =>
const dxt5ToAbgr8888 = (width: number, height: number, input: Uint8Array) =>
dxtToAbgr8888(width, height, input, DXT5_BLOCK_SIZE, dxt5DecompressBlock);

export { dxt1ToAbgr8888, dxt3ToAbgr8888, dxt5ToAbgr8888 };
export { dxt1ToAbgr8888, dxt3ToAbgr8888, dxt5ToAbgr8888, getDxt1Size, getDxt3Size, getDxt5Size };
18 changes: 17 additions & 1 deletion src/lib/blp/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ const calcMipLevelCount = (width: number, height: number) => {

const getSizeAtMipLevel = (size: number, level: number) => (size / (1 << level)) | 0;

const getResizedBytes = (bytes: Uint8Array, size: number) => {
if (bytes.byteLength === size) {
return bytes;
}

if (bytes.byteLength < size) {
const padded = new Uint8Array(size);
padded.set(bytes, 0);
return padded;
}

const trimmed = new Uint8Array(size);
trimmed.set(bytes.subarray(0, size), 0);
return trimmed;
};

const resizeBilinear = (
data: Uint8Array,
width: number,
Expand Down Expand Up @@ -63,4 +79,4 @@ const resizeBilinear = (
return newData;
};

export { calcMipLevelCount, getSizeAtMipLevel, resizeBilinear };
export { calcMipLevelCount, getSizeAtMipLevel, getResizedBytes, resizeBilinear };

0 comments on commit 5d83376

Please sign in to comment.