refactor: 组合器固定输出结果大小而非可变

This commit is contained in:
unanmed 2025-10-20 18:27:10 +08:00
parent 66e60f5f15
commit 146866d51f
2 changed files with 39 additions and 69 deletions

View File

@ -21,7 +21,7 @@ import { isNil } from 'lodash-es';
interface IndexMarkedComposedData { interface IndexMarkedComposedData {
/** 组合数据 */ /** 组合数据 */
readonly data: ITextureComposedData; readonly asset: ITextureComposedData;
/** 组合时最后一个用到的贴图的索引 */ /** 组合时最后一个用到的贴图的索引 */
readonly index: number; readonly index: number;
} }
@ -33,75 +33,37 @@ type TranslatedComposer<D, T> = ITextureComposer<
>; >;
export interface IGridComposerData { export interface IGridComposerData {
/** 单张图集的最大宽度 */
readonly maxWidth: number;
/** 单张图集的最大高度 */
readonly maxHeight: number;
/** 单个贴图的宽度,与之不同的贴图将会被剔除并警告 */ /** 单个贴图的宽度,与之不同的贴图将会被剔除并警告 */
readonly width: number; readonly width: number;
/** 单个贴图的宽度,与之不同的贴图将会被剔除并警告 */ /** 单个贴图的宽度,与之不同的贴图将会被剔除并警告 */
readonly height: number; readonly height: number;
} }
interface GridNeededSize {
/** 图集宽度 */
readonly width: number;
/** 图集高度 */
readonly height: number;
/** 一共有多少行 */
readonly rows: number;
/** 一共有多少列 */
readonly cols: number;
}
/**
*
*
*/
export class TextureGridComposer<T> export class TextureGridComposer<T>
implements TranslatedComposer<IGridComposerData, T> implements TranslatedComposer<IGridComposerData, T>
{ {
private getNeededSize( /**
tex: ITexture[], *
start: number, *
data: IGridComposerData * @param maxWidth
): GridNeededSize { * @param maxHeight
const maxRows = Math.floor(data.maxWidth / data.width); */
const maxCols = Math.floor(data.maxHeight / data.height); constructor(
const rest = tex.length - start; readonly maxWidth: number,
if (rest >= maxRows * maxCols) { readonly maxHeight: number
const size: GridNeededSize = { ) {}
width: data.maxWidth,
height: data.maxHeight,
rows: maxRows,
cols: maxCols
};
return size;
} else {
const aspect = data.height / data.width;
const cols = Math.ceil(Math.sqrt(rest * aspect));
const rows = Math.ceil(rest / cols);
const size: GridNeededSize = {
width: cols * data.width,
height: rows * data.height,
rows,
cols
};
return size;
}
}
private nextAsset( private nextAsset(
tex: ITexture[], tex: ITexture[],
start: number, start: number,
data: IGridComposerData data: IGridComposerData,
rows: number,
cols: number
): IndexMarkedComposedData { ): IndexMarkedComposedData {
const size = this.getNeededSize(tex, start, data);
const { width, height, rows, cols } = size;
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')!; const ctx = canvas.getContext('2d')!;
canvas.width = width; canvas.width = this.maxWidth;
canvas.height = height; canvas.height = this.maxHeight;
const count = Math.min(rows * cols, tex.length - start); const count = Math.min(rows * cols, tex.length - start);
const map = new Map<ITexture, IRect>(); const map = new Map<ITexture, IRect>();
@ -131,7 +93,7 @@ export class TextureGridComposer<T>
assetMap: map assetMap: map
}; };
return { data: composed, index: start + count }; return { asset: composed, index: start + count };
} }
*compose( *compose(
@ -140,10 +102,13 @@ export class TextureGridComposer<T>
): Generator<ITextureComposedData> { ): Generator<ITextureComposedData> {
const arr = [...input]; const arr = [...input];
const rows = Math.floor(this.maxWidth / data.width);
const cols = Math.floor(this.maxHeight / data.height);
let i = 0; let i = 0;
while (i < arr.length) { while (i < arr.length) {
const { data: asset, index } = this.nextAsset(arr, i, data); const { asset, index } = this.nextAsset(arr, i, data, rows, cols);
i = index + 1; i = index + 1;
yield asset; yield asset;
} }
@ -160,13 +125,15 @@ interface MaxRectsRectangle extends IRectangle {
readonly data: ITexture; readonly data: ITexture;
} }
/**
* 使 Max Rects {@link IMaxRectsComposerData}
* {@link TextureMaxRectsWebGL2Composer}
*/
export class TextureMaxRectsComposer<T> export class TextureMaxRectsComposer<T>
implements TranslatedComposer<IMaxRectsComposerData, T> implements TranslatedComposer<IMaxRectsComposerData, T>
{ {
/**
* 使 Max Rects {@link IMaxRectsComposerData}
* {@link TextureMaxRectsWebGL2Composer}
* @param maxWidth
* @param maxHeight
*/
constructor( constructor(
public readonly maxWidth: number, public readonly maxWidth: number,
public readonly maxHeight: number public readonly maxHeight: number
@ -195,8 +162,8 @@ export class TextureMaxRectsComposer<T>
const map = new Map<ITexture, IRect>(); const map = new Map<ITexture, IRect>();
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')!; const ctx = canvas.getContext('2d')!;
canvas.width = bin.width; canvas.width = this.maxWidth;
canvas.height = bin.height; canvas.height = this.maxHeight;
ctx.imageSmoothingEnabled = false; ctx.imageSmoothingEnabled = false;
bin.rects.forEach(v => { bin.rects.forEach(v => {
const rect: IRect = { x: v.x, y: v.y, w: v.width, h: v.height }; const rect: IRect = { x: v.x, y: v.y, w: v.width, h: v.height };
@ -226,12 +193,6 @@ interface RectProcessed {
readonly attrib: Float32Array; readonly attrib: Float32Array;
} }
/**
* 使 Max Rects {@link IMaxRectsComposerData}
*
* `compose`
* 使 `toBitmap`
*/
export class TextureMaxRectsWebGL2Composer<T> export class TextureMaxRectsWebGL2Composer<T>
implements TranslatedComposer<IMaxRectsComposerData, T> implements TranslatedComposer<IMaxRectsComposerData, T>
{ {
@ -257,6 +218,15 @@ export class TextureMaxRectsWebGL2Composer<T>
/** 本次处理的贴图高度 */ /** 本次处理的贴图高度 */
private opHeight: number = 0; private opHeight: number = 0;
/**
* 使 Max Rects 使 WebGL2
* {@link IMaxRectsComposerData}
* `compose`
* 使 `toBitmap`,
* `next`
* @param maxWidth
* @param maxHeight
*/
constructor( constructor(
public readonly maxWidth: number, public readonly maxWidth: number,
public readonly maxHeight: number public readonly maxHeight: number

View File

@ -50,7 +50,7 @@ export class Texture<T = unknown, A = unknown> implements ITexture<T, A> {
async toBitmap(): Promise<void> { async toBitmap(): Promise<void> {
if (this.source instanceof ImageBitmap) return; if (this.source instanceof ImageBitmap) return;
this.source = await createImageBitmap(this.source as any); this.source = await createImageBitmap(this.source);
} }
split<U>(splitter: ITextureSplitter<U>, data: U): Generator<ITexture> { split<U>(splitter: ITextureSplitter<U>, data: U): Generator<ITexture> {