move image drawing to core
also adjust for some api changes I forgot to fix
This commit is contained in:
parent
f8511894e3
commit
d97004a6dd
5 changed files with 58 additions and 39 deletions
49
msagent.js/core/src/image.ts
Normal file
49
msagent.js/core/src/image.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { AcsImageEntry } from './structs/image';
|
||||||
|
|
||||||
|
// probably should be in a utility module
|
||||||
|
function dwAlign(off: number): number {
|
||||||
|
let ul = off >>> 0;
|
||||||
|
|
||||||
|
ul += 3;
|
||||||
|
ul >>= 2;
|
||||||
|
ul <<= 2;
|
||||||
|
return ul;
|
||||||
|
}
|
||||||
|
|
||||||
|
import { BufferStream, SeekDir } from './buffer';
|
||||||
|
import { RGBAColor } from './structs/core';
|
||||||
|
|
||||||
|
/// Draws an ACS image to a newly allocated buffer.
|
||||||
|
/// This function normalizes the agent 8bpp DIB format to a saner RGBA format,
|
||||||
|
/// that can be directly converted to an ImageData for drawing to a web canvas.
|
||||||
|
///
|
||||||
|
/// However, that should be done (and is done) by a higher level web layer.
|
||||||
|
export function imageDrawToBuffer(imageEntry: AcsImageEntry, palette: RGBAColor[]) {
|
||||||
|
let rgbaBuffer = new Uint32Array(imageEntry.image.width * imageEntry.image.height);
|
||||||
|
|
||||||
|
let buffer = imageEntry.image.data;
|
||||||
|
let bufStream = new BufferStream(buffer);
|
||||||
|
|
||||||
|
let rows = new Array<Uint8Array>(imageEntry.image.height - 1);
|
||||||
|
|
||||||
|
// Read all the rows bottom-up first. This idiosyncracy is due to the fact
|
||||||
|
// that the bitmap data is actually formatted to be used as a GDI DIB
|
||||||
|
// (device-independent bitmap), so it inherits all the strange baggage from that.
|
||||||
|
for (let y = imageEntry.image.height - 1; y >= 0; --y) {
|
||||||
|
let row = bufStream.subBuffer(imageEntry.image.width).raw();
|
||||||
|
rows[y] = row.slice(0, imageEntry.image.width);
|
||||||
|
|
||||||
|
// Seek to the next DWORD aligned spot to get to the next row.
|
||||||
|
// For most images this may mean not seeking at all.
|
||||||
|
bufStream.seek(dwAlign(bufStream.tell()), SeekDir.BEG);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, draw the rows converted to RGBA, top down (so it's drawn as you'd expect)
|
||||||
|
for (let y = 0; y < imageEntry.image.height - 1; ++y) {
|
||||||
|
let row = rows[y];
|
||||||
|
for (let x = 0; x < imageEntry.image.width; ++x) {
|
||||||
|
rgbaBuffer[y * imageEntry.image.width + x] = palette[row[x]].to_rgba();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rgbaBuffer;
|
||||||
|
}
|
|
@ -9,4 +9,5 @@ export * from './structs/image.js';
|
||||||
export * from './types.js';
|
export * from './types.js';
|
||||||
export * from './decompress.js';
|
export * from './decompress.js';
|
||||||
|
|
||||||
|
export * from './image.js';
|
||||||
export * from './parse.js';
|
export * from './parse.js';
|
||||||
|
|
|
@ -1,20 +1,12 @@
|
||||||
import { BufferStream, SeekDir } from '@msagent.js/core';
|
import { BufferStream, SeekDir, imageDrawToBuffer } from '@msagent.js/core';
|
||||||
import { AcsData } from './character.js';
|
import { AcsData } from '@msagent.js/core';
|
||||||
import { ContextMenu, ContextMenuItem } from './contextmenu.js';
|
import { ContextMenu, ContextMenuItem } from './contextmenu.js';
|
||||||
import { AcsAnimation, AcsAnimationFrameInfo } from '@msagent.js/core';
|
import { AcsAnimation, AcsAnimationFrameInfo } from '@msagent.js/core';
|
||||||
import { AcsImageEntry } from '@msagent.js/core';
|
import { AcsImageEntry } from '@msagent.js/core';
|
||||||
import { Point, Size } from '@msagent.js/core';
|
import { Point, Size } from '@msagent.js/core';
|
||||||
import { wordballoonDrawImage, wordballoonDrawText } from './wordballoon.js';
|
import { wordballoonDrawImage, wordballoonDrawText } from './wordballoon.js';
|
||||||
|
|
||||||
// probably should be in a utility module
|
|
||||||
function dwAlign(off: number): number {
|
|
||||||
let ul = off >>> 0;
|
|
||||||
|
|
||||||
ul += 3;
|
|
||||||
ul >>= 2;
|
|
||||||
ul <<= 2;
|
|
||||||
return ul;
|
|
||||||
}
|
|
||||||
|
|
||||||
function randint(min: number, max: number) {
|
function randint(min: number, max: number) {
|
||||||
return Math.floor(Math.random() * (max - min) + min);
|
return Math.floor(Math.random() * (max - min) + min);
|
||||||
|
@ -316,34 +308,7 @@ export class Agent {
|
||||||
|
|
||||||
// Draw a single image from the agent's image table.
|
// Draw a single image from the agent's image table.
|
||||||
drawImage(imageEntry: AcsImageEntry, xOffset: number, yOffset: number) {
|
drawImage(imageEntry: AcsImageEntry, xOffset: number, yOffset: number) {
|
||||||
let rgbaBuffer = new Uint32Array(imageEntry.image.width * imageEntry.image.height);
|
let rgbaBuffer = imageDrawToBuffer(imageEntry, this.data.characterInfo.palette);
|
||||||
|
|
||||||
let buffer = imageEntry.image.data;
|
|
||||||
let bufStream = new BufferStream(buffer);
|
|
||||||
|
|
||||||
let rows = new Array<Uint8Array>(imageEntry.image.height - 1);
|
|
||||||
|
|
||||||
// Read all the rows bottom-up first. This idiosyncracy is due to the fact
|
|
||||||
// that the bitmap data is actually formatted to be used as a GDI DIB
|
|
||||||
// (device-independent bitmap), so it inherits all the strange baggage from that.
|
|
||||||
for (let y = imageEntry.image.height - 1; y >= 0; --y) {
|
|
||||||
let row = bufStream.subBuffer(imageEntry.image.width).raw();
|
|
||||||
let rowResized = row.slice(0, imageEntry.image.width);
|
|
||||||
rows[y] = rowResized;
|
|
||||||
|
|
||||||
// Seek to the next DWORD aligned spot to get to the next row.
|
|
||||||
// For most images this may mean not seeking at all.
|
|
||||||
bufStream.seek(dwAlign(bufStream.tell()), SeekDir.BEG);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, draw the rows converted to RGBA, top down (so it's drawn as you'd expect)
|
|
||||||
for (let y = 0; y < imageEntry.image.height - 1; ++y) {
|
|
||||||
let row = rows[y];
|
|
||||||
for (let x = 0; x < imageEntry.image.width; ++x) {
|
|
||||||
rgbaBuffer[y * imageEntry.image.width + x] = this.data.characterInfo.palette[row[x]].to_rgba();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = new ImageData(new Uint8ClampedArray(rgbaBuffer.buffer), imageEntry.image.width, imageEntry.image.height);
|
let data = new ImageData(new Uint8ClampedArray(rgbaBuffer.buffer), imageEntry.image.width, imageEntry.image.height);
|
||||||
this.ctx.putImageData(data, xOffset, yOffset);
|
this.ctx.putImageData(data, xOffset, yOffset);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { compressInit } from '@msagent.js/core';
|
import { compressInit } from '@msagent.js/core';
|
||||||
import { wordballoonInit } from './wordballoon.js';
|
import { wordballoonInit } from './wordballoon.js';
|
||||||
|
|
||||||
|
export { Agent } from './agent.js';
|
||||||
export * from './character.js';
|
export * from './character.js';
|
||||||
export * from './sprite.js';
|
export * from './sprite.js';
|
||||||
export * from './wordballoon.js';
|
export * from './wordballoon.js';
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// This will go away when it isn't needed
|
// This will go away when it isn't needed
|
||||||
|
|
||||||
import * as msagent from '@msagent.js/web';
|
import * as msagent from '@msagent.js/web';
|
||||||
|
import * as msagent_core from '@msagent.js/core';
|
||||||
|
|
||||||
let w = window as any;
|
let w = window as any;
|
||||||
w.agents = [];
|
w.agents = [];
|
||||||
let input = document.getElementById('testbed-input') as HTMLInputElement;
|
let input = document.getElementById('testbed-input') as HTMLInputElement;
|
||||||
|
@ -12,7 +14,8 @@ input.addEventListener('change', async () => {
|
||||||
let buffer = await input.files![0].arrayBuffer();
|
let buffer = await input.files![0].arrayBuffer();
|
||||||
|
|
||||||
console.log('Creating agent');
|
console.log('Creating agent');
|
||||||
let agent = msagent.agentCreateCharacter(new Uint8Array(buffer));
|
let agentParsedData = msagent_core.agentCharacterParseACS(new msagent_core.BufferStream(new Uint8Array(buffer)));
|
||||||
|
let agent = new msagent.Agent(agentParsedData);
|
||||||
|
|
||||||
w.agents.push(agent);
|
w.agents.push(agent);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue