refactor: move DDS cache

moved the DDS cache from dds.ts to ddsCache.ts and added caching for scaled images

Co-authored-by: split / May <split@split.pet>
pull/97/head
Raymond 2025-01-01 16:30:48 -05:00
parent ce95f2165d
commit 4d4335004f
2 changed files with 85 additions and 29 deletions

View File

@ -8,6 +8,8 @@ DDS header parsing based off of https://gist.github.com/brett19/13c83c2e5e389337
*/
import DDSCache from "./ddsCache";
function makeFourCC(string: string) {
return string.charCodeAt(0) +
(string.charCodeAt(1) << 8) +
@ -56,7 +58,7 @@ void main() {
export class DDS {
constructor(db: IDBDatabase | undefined) {
this.db = db
this.cache = new DDSCache(db);
let gl = this.canvasGL.getContext("webgl");
if (!gl) throw new Error("Failed to get WebGL rendering context") // TODO: make it switch to Classic userbox
@ -200,20 +202,10 @@ export class DDS {
*/
loadFile(path: string) : Promise<boolean> {
return new Promise(async r => {
if (!this.db)
return r(false);
let transaction = this.db.transaction(["dds"], "readonly");
let objectStore = transaction.objectStore("dds");
let request = objectStore.get(path);
request.onsuccess = async (e) => {
if (request.result)
if (request.result.blob) {
await this.fromBlob(request.result.blob)
return r(true);
}
r(false);
}
request.onerror = () => r(false);
let file = await this.cache?.getFromDatabase(path)
if (file != null)
await this.fromBlob(file)
r(file != null)
})
};
@ -224,17 +216,19 @@ export class DDS {
* @returns An object URL which correlates to a Blob
*/
async getFile(path: string, fallback?: string) : Promise<string> {
if (this.urlCache[path])
return this.urlCache[path]
if (this.cache?.cached(path))
return this.cache.find(path) ?? ""
if (!await this.loadFile(path))
if (fallback) {
if (!await this.loadFile(fallback))
return "";
} else
return "";
let url = URL.createObjectURL(await this.getBlob("image/png") ?? new Blob([]));
this.urlCache[path] = url;
return url
return ""
let blob = await this.getBlob("image/png");
if (!blob) return ""
return this.cache?.save(
path, URL.createObjectURL(blob)
) ?? "";
};
/**
@ -254,6 +248,7 @@ export class DDS {
this.canvas2D.height = h * (s ?? 1);
this.ctx.drawImage(this.canvasGL, x, y, w, h, 0, 0, w * (s ?? 1), h * (s ?? 1));
/* We don't want to cache this, it's a spritesheet piece. */
return URL.createObjectURL(await this.get2DBlob("image/png") ?? new Blob([]));
};
@ -265,8 +260,8 @@ export class DDS {
* @returns An object URL which correlates to a Blob
*/
async getFileScaled(path: string, s: number, fallback?: string): Promise<string> {
if (this.urlCache[path])
return this.urlCache[path]
if (this.cache?.cached(path, s))
return this.cache.find(path, s) ?? ""
if (!await this.loadFile(path))
if (fallback) {
if (!await this.loadFile(fallback))
@ -276,9 +271,8 @@ export class DDS {
this.canvas2D.width = this.canvasGL.width * (s ?? 1);
this.canvas2D.height = this.canvasGL.height * (s ?? 1);
this.ctx.drawImage(this.canvasGL, 0, 0, this.canvasGL.width, this.canvasGL.height, 0, 0, this.canvasGL.width * (s ?? 1), this.canvasGL.height * (s ?? 1));
let url = URL.createObjectURL(await this.get2DBlob("image/png") ?? new Blob([]));
this.urlCache[path] = url;
return url;
return this.cache?.save(path, URL.createObjectURL(await this.get2DBlob("image/png") ?? new Blob([])), s) ?? "";
};
/**
@ -332,13 +326,11 @@ export class DDS {
canvas2D: HTMLCanvasElement = document.createElement("canvas");
canvasGL: HTMLCanvasElement = document.createElement("canvas");
urlCache: Record<string, string> = {};
cache: DDSCache | null;
ctx: CanvasRenderingContext2D;
gl: WebGLRenderingContext;
ext: ReturnType<typeof this.gl.getExtension>;
shader: WebGLShader | null = null;
db: IDBDatabase | undefined;
};

View File

@ -0,0 +1,64 @@
export default class DDSCache {
constructor(db: IDBDatabase | undefined) {
this.db = db;
}
/**
* @description Finds an object URL for the image with the specified path and scale
* @param path Image path
* @param scale Scale factor
*/
find(path: string, scale: number = 1): string | undefined {
return (this.urlCache.find(
p => p.path == path && p.scale == scale)?.url)
}
/**
* @description Checks whether an object URL is cached for the image with the specified path and scale
* @param path Image path
* @param scale Scale factor
*/
cached(path: string, scale: number = 1): boolean {
return this.urlCache.some(
p => p.path == path && p.scale == scale)
}
/**
* @description Save an object URL for the specified path and scale to the cache
* @param path Image path
* @param url Object URL
* @param scale Scale factor
*/
save(path: string, url: string, scale: number = 1) {
if (this.cached(path, scale)) {
URL.revokeObjectURL(url);
return this.find(path, scale)
}
this.urlCache.push({path, url, scale})
return url
}
/**
* @description Retrieve a Blob from a database based on the specified path
* @param path Image path
*/
getFromDatabase(path: string): Promise<Blob | null> {
return new Promise((resolve, reject) => {
if (!this.db)
return resolve(null);
let transaction = this.db.transaction(["dds"], "readonly");
let objectStore = transaction.objectStore("dds");
let request = objectStore.get(path);
request.onsuccess = async (e) => {
if (request.result)
if (request.result.blob)
return resolve(request.result.blob);
return resolve(null);
}
request.onerror = () => resolve(null);
})
};
private urlCache: {scale: number, path: string, url: string}[] = [];
private db: IDBDatabase | undefined;
}