Fix colors

This commit is contained in:
Bill 2021-09-03 22:48:36 -04:00
parent ff5931fa18
commit a0400b7a88
15 changed files with 398 additions and 89 deletions

View File

@ -305,6 +305,8 @@ export class AvatarImage implements IAvatarImage
partCount--; partCount--;
} }
//CanvasUtilities.cropTransparentPixels(canvas);
//if(this._avatarSpriteData && this._avatarSpriteData.paletteIsGrayscale) this.convertToGrayscale(container); //if(this._avatarSpriteData && this._avatarSpriteData.paletteIsGrayscale) this.convertToGrayscale(container);
return canvas; return canvas;

View File

@ -12,6 +12,7 @@ import { AvatarCanvas } from '../structure';
import { AvatarImageActionCache } from './AvatarImageActionCache'; import { AvatarImageActionCache } from './AvatarImageActionCache';
import { AvatarImageBodyPartCache } from './AvatarImageBodyPartCache'; import { AvatarImageBodyPartCache } from './AvatarImageBodyPartCache';
import { AvatarImageDirectionCache } from './AvatarImageDirectionCache'; import { AvatarImageDirectionCache } from './AvatarImageDirectionCache';
import { CompleteImageData } from './CompleteImageData';
import { ImageData } from './ImageData'; import { ImageData } from './ImageData';
export class AvatarImageCache export class AvatarImageCache
@ -416,7 +417,7 @@ export class AvatarImageCache
imageIndex--; imageIndex--;
} }
return new AvatarImageBodyPartContainer(imageData.texture, offset, isCacheable); return new AvatarImageBodyPartContainer(imageData.image, offset, isCacheable);
} }
private convertColorToHex(k: number): string private convertColorToHex(k: number): string
@ -429,7 +430,7 @@ export class AvatarImageCache
return _local_2; return _local_2;
} }
private createUnionImage(imageDatas: ImageData[], isFlipped: boolean): ImageData private createUnionImage(imageDatas: ImageData[], isFlipped: boolean): CompleteImageData
{ {
const bounds = new Rectangle(); const bounds = new Rectangle();
@ -470,10 +471,11 @@ export class AvatarImageCache
ty = (regPoint.y - data.rect.y); ty = (regPoint.y - data.rect.y);
} }
const tintedTexture = texture.getTintedWithMultiply(color);
ctx.save(); ctx.save();
//ctx.fillStyle = ('#' + this.convertColorToHex(data.colorTransform));
ctx.transform(scale, 0, 0, 1, tx, ty); ctx.transform(scale, 0, 0, 1, tx, ty);
ctx.drawImage(texture, 0, 0, data.rect.width, data.rect.height,); ctx.drawImage(tintedTexture, 0, 0, data.rect.width, data.rect.height);
ctx.restore(); ctx.restore();
// set the color // set the color
@ -481,6 +483,6 @@ export class AvatarImageCache
//console.log(); //console.log();
} }
return new ImageData(canvas, new Rectangle(0, 0, canvas.width, canvas.height), point, isFlipped, null); return new CompleteImageData(canvas, new Rectangle(0, 0, canvas.width, canvas.height), point, isFlipped, null);
} }
} }

View File

@ -0,0 +1,59 @@
import { Canvas } from 'canvas';
import { Point, Rectangle } from '../../../core';
export class CompleteImageData
{
private _image: Canvas;
private _rect: Rectangle;
private _regPoint: Point;
private _flipH: boolean;
private _colorTransform: number;
constructor(texture: Canvas, rectangle: Rectangle, _arg_3: Point, flipH: boolean, color: number)
{
this._image = texture;
this._rect = rectangle;
this._regPoint = _arg_3;
this._flipH = flipH;
this._colorTransform = color;
if(flipH) this._regPoint.x = (-(this._regPoint.x) + rectangle.width);
}
public dispose(): void
{
this._image = null;
this._regPoint = null;
this._colorTransform = null;
}
public get image(): Canvas
{
return this._image;
}
public get rect(): Rectangle
{
return this._rect;
}
public get regPoint(): Point
{
return this._regPoint;
}
public get flipH(): boolean
{
return this._flipH;
}
public get colorTransform(): number
{
return this._colorTransform;
}
public get offsetRect(): Rectangle
{
return new Rectangle(-(this._regPoint.x), -(this._regPoint.y), this._rect.width, this._rect.height);
}
}

View File

@ -1,15 +1,14 @@
import { Canvas } from 'canvas'; import { Point, Rectangle, Texture } from '../../../core';
import { Point, Rectangle } from '../../../core';
export class ImageData export class ImageData
{ {
private _texture: Canvas; private _texture: Texture;
private _rect: Rectangle; private _rect: Rectangle;
private _regPoint: Point; private _regPoint: Point;
private _flipH: boolean; private _flipH: boolean;
private _colorTransform: number; private _colorTransform: number;
constructor(texture: Canvas, rectangle: Rectangle, _arg_3: Point, flipH: boolean, color: number) constructor(texture: Texture, rectangle: Rectangle, _arg_3: Point, flipH: boolean, color: number)
{ {
this._texture = texture; this._texture = texture;
this._rect = rectangle; this._rect = rectangle;
@ -27,7 +26,7 @@ export class ImageData
this._colorTransform = null; this._colorTransform = null;
} }
public get texture(): Canvas public get texture(): Texture
{ {
return this._texture; return this._texture;
} }

View File

@ -2,4 +2,5 @@ export * from './AvatarImageActionCache';
export * from './AvatarImageBodyPartCache'; export * from './AvatarImageBodyPartCache';
export * from './AvatarImageCache'; export * from './AvatarImageCache';
export * from './AvatarImageDirectionCache'; export * from './AvatarImageDirectionCache';
export * from './CompleteImageData';
export * from './ImageData'; export * from './ImageData';

View File

@ -1,13 +1,12 @@
import { Canvas } from 'canvas';
import { NitroManager } from '../common'; import { NitroManager } from '../common';
import { AdvancedMap, FileUtilities } from '../utils'; import { AdvancedMap, FileUtilities, Texture } from '../utils';
import { IAssetManager } from './IAssetManager'; import { IAssetManager } from './IAssetManager';
import { NitroBundle } from './NitroBundle'; import { NitroBundle } from './NitroBundle';
import { GraphicAssetCollection, IGraphicAsset, IGraphicAssetCollection } from './utils'; import { GraphicAssetCollection, IGraphicAsset, IGraphicAssetCollection } from './utils';
export class AssetManager extends NitroManager implements IAssetManager export class AssetManager extends NitroManager implements IAssetManager
{ {
private _textures: AdvancedMap<string, Canvas>; private _textures: AdvancedMap<string, Texture>;
private _collections: AdvancedMap<string, IGraphicAssetCollection>; private _collections: AdvancedMap<string, IGraphicAssetCollection>;
constructor() constructor()
@ -18,7 +17,7 @@ export class AssetManager extends NitroManager implements IAssetManager
this._collections = new AdvancedMap(); this._collections = new AdvancedMap();
} }
public getTexture(name: string): Canvas public getTexture(name: string): Texture
{ {
if(!name) return null; if(!name) return null;
@ -29,7 +28,7 @@ export class AssetManager extends NitroManager implements IAssetManager
return existing; return existing;
} }
public setTexture(name: string, texture: Canvas): void public setTexture(name: string, texture: Texture): void
{ {
if(!name || !texture) return; if(!name || !texture) return;

View File

@ -1,12 +1,12 @@
import { Canvas } from 'canvas';
import { INitroManager } from '../common'; import { INitroManager } from '../common';
import { AdvancedMap, Texture } from '../utils';
import { NitroBundle } from './NitroBundle'; import { NitroBundle } from './NitroBundle';
import { IGraphicAsset, IGraphicAssetCollection } from './utils'; import { IGraphicAsset, IGraphicAssetCollection } from './utils';
export interface IAssetManager extends INitroManager export interface IAssetManager extends INitroManager
{ {
getTexture(name: string): Canvas; getTexture(name: string): Texture;
setTexture(name: string, texture: Canvas): void; setTexture(name: string, texture: Texture): void;
getAsset(name: string): IGraphicAsset; getAsset(name: string): IGraphicAsset;
getCollection(name: string): IGraphicAssetCollection; getCollection(name: string): IGraphicAssetCollection;
createCollectionFromNitroBundle(bundle: NitroBundle): IGraphicAssetCollection; createCollectionFromNitroBundle(bundle: NitroBundle): IGraphicAssetCollection;

View File

@ -1,5 +1,4 @@
import { Canvas } from 'canvas'; import { Rectangle, Texture } from '../../utils';
import { Rectangle } from '../../utils';
import { IGraphicAsset } from './IGraphicAsset'; import { IGraphicAsset } from './IGraphicAsset';
export class GraphicAsset implements IGraphicAsset export class GraphicAsset implements IGraphicAsset
@ -8,7 +7,7 @@ export class GraphicAsset implements IGraphicAsset
private _name: string; private _name: string;
private _source: string; private _source: string;
private _texture: Canvas; private _texture: Texture;
private _usesPalette: boolean; private _usesPalette: boolean;
private _x: number; private _x: number;
private _y: number; private _y: number;
@ -19,7 +18,7 @@ export class GraphicAsset implements IGraphicAsset
private _rectangle: Rectangle; private _rectangle: Rectangle;
private _initialized: boolean; private _initialized: boolean;
public static createAsset(name: string, source: string, texture: Canvas, x: number, y: number, flipH: boolean = false, flipV: boolean = false, usesPalette: boolean = false): GraphicAsset public static createAsset(name: string, source: string, texture: Texture, x: number, y: number, flipH: boolean = false, flipV: boolean = false, usesPalette: boolean = false): GraphicAsset
{ {
const graphicAsset = (GraphicAsset.GRAPHIC_POOL.length ? GraphicAsset.GRAPHIC_POOL.pop() : new GraphicAsset()); const graphicAsset = (GraphicAsset.GRAPHIC_POOL.length ? GraphicAsset.GRAPHIC_POOL.pop() : new GraphicAsset());
@ -74,7 +73,7 @@ export class GraphicAsset implements IGraphicAsset
return this._source; return this._source;
} }
public get texture(): Canvas public get texture(): Texture
{ {
return this._texture; return this._texture;
} }

View File

@ -1,5 +1,5 @@
import { Canvas, createCanvas, Image } from 'canvas'; import { Image } from 'canvas';
import { AdvancedMap, Rectangle } from '../../utils'; import { AdvancedMap, BaseTexture, Rectangle, Texture } from '../../utils';
import { IAsset, IAssetData, IAssetPalette } from '../interfaces'; import { IAsset, IAssetData, IAssetPalette } from '../interfaces';
import { GraphicAsset } from './GraphicAsset'; import { GraphicAsset } from './GraphicAsset';
import { GraphicAssetPalette } from './GraphicAssetPalette'; import { GraphicAssetPalette } from './GraphicAssetPalette';
@ -12,12 +12,12 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
private _name: string; private _name: string;
private _data: IAssetData; private _data: IAssetData;
private _textures: AdvancedMap<string, Canvas>; private _textures: AdvancedMap<string, Texture>;
private _assets: AdvancedMap<string, GraphicAsset>; private _assets: AdvancedMap<string, GraphicAsset>;
private _palettes: AdvancedMap<string, GraphicAssetPalette>; private _palettes: AdvancedMap<string, GraphicAssetPalette>;
private _paletteAssetNames: string[]; private _paletteAssetNames: string[];
constructor(data: IAssetData, baseTexture: Image) constructor(data: IAssetData, image: Image)
{ {
if(!data) throw new Error('invalid_collection'); if(!data) throw new Error('invalid_collection');
@ -28,7 +28,12 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
this._palettes = new AdvancedMap(); this._palettes = new AdvancedMap();
this._paletteAssetNames = []; this._paletteAssetNames = [];
if(baseTexture) this.processBaseTexture(baseTexture); if(image)
{
const baseTexture = new BaseTexture(image);
this.processBaseTexture(baseTexture);
}
this.define(data); this.define(data);
} }
@ -57,7 +62,7 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
} }
} }
private processBaseTexture(baseTexture: Image): void private processBaseTexture(baseTexture: BaseTexture): void
{ {
const frames = this._data.spritesheet.frames; const frames = this._data.spritesheet.frames;
@ -107,58 +112,9 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
); );
} }
const width = frame.width; const texture = new Texture(baseTexture, frame, orig, trim);
const height = frame.height;
let dx = 0; this._textures.add(name, texture);
let dy = 0;
if(trim)
{
dx = (trim.width / 2) + trim.x - (0 * orig.width);
dy = (trim.height / 2) + trim.y - (0 * orig.height);
}
else
{
dx = (0.5 - 0) * orig.width;
dy = (0.5 - 0) * orig.height;
}
dx -= width / 2;
dy -= height / 2;
const canvas = createCanvas(sourceSize.w, sourceSize.h);
const ctx = canvas.getContext('2d');
ctx.drawImage(
baseTexture,
frame.x * resolution,
frame.y * resolution,
Math.floor(width * resolution),
Math.floor(height * resolution),
Math.floor(dx * resolution),
Math.floor(dy * resolution),
Math.floor(width * resolution),
Math.floor(height * resolution)
);
if(name.indexOf('h_std_hr_4_2_0') >= 0)
{
//console.log(spritesheetFrame);
console.log('h_std_hr_4_2_0');
console.log(canvas.toDataURL());
console.log();
}
if(name.indexOf('h_std_hrb_4_2_0') >= 0)
{
//console.log(frameData, source);
console.log('h_std_hrb_4_2_0');
console.log(canvas.toDataURL());
console.log();
}
this._textures.add(name, canvas);
} }
} }
@ -242,7 +198,7 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
} }
} }
private createAsset(name: string, source: string, texture: Canvas, flipH: boolean, flipV: boolean, x: number, y: number, usesPalette: boolean): boolean private createAsset(name: string, source: string, texture: Texture, flipH: boolean, flipV: boolean, x: number, y: number, usesPalette: boolean): boolean
{ {
if(this._assets.getValue(name)) return false; if(this._assets.getValue(name)) return false;
@ -253,7 +209,7 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
return true; return true;
} }
private replaceAsset(name: string, source: string, texture: Canvas, flipH: boolean, flipV: boolean, x: number, y: number, usesPalette: boolean): boolean private replaceAsset(name: string, source: string, texture: Texture, flipH: boolean, flipV: boolean, x: number, y: number, usesPalette: boolean): boolean
{ {
const existing = this._assets.getValue(name); const existing = this._assets.getValue(name);
@ -350,7 +306,7 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
existing.recycle(); existing.recycle();
} }
public getLibraryAsset(name: string): Canvas public getLibraryAsset(name: string): Texture
{ {
if(!name) return null; if(!name) return null;
@ -386,7 +342,7 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
return this._data; return this._data;
} }
public get textures(): AdvancedMap<string, Canvas> public get textures(): AdvancedMap<string, Texture>
{ {
return this._textures; return this._textures;
} }

View File

@ -1,4 +1,4 @@
import { Canvas } from 'canvas'; import { Texture } from '../../utils';
export class GraphicAssetPalette export class GraphicAssetPalette
{ {
@ -21,7 +21,7 @@ export class GraphicAssetPalette
} }
public applyPalette(texture: Canvas): Canvas public applyPalette(texture: Texture): Texture
{ {
return null; return null;

View File

@ -1,11 +1,10 @@
import { Canvas } from 'canvas'; import { Rectangle, Texture } from '../../utils';
import { Rectangle } from '../../utils';
export interface IGraphicAsset export interface IGraphicAsset
{ {
name: string; name: string;
source: string; source: string;
texture: Canvas; texture: Texture;
usesPalette: boolean; usesPalette: boolean;
x: number; x: number;
y: number; y: number;

View File

@ -0,0 +1,28 @@
import { Image } from 'canvas';
export class BaseTexture
{
private _image: Image;
private _resolution: number;
constructor(image: Image)
{
this._image = image;
this._resolution = 1;
}
public getDrawableSource(): Image
{
return this._image;
}
public get image(): Image
{
return this._image;
}
public get resolution(): number
{
return this._resolution;
}
}

View File

@ -0,0 +1,102 @@
import { Canvas, createCanvas } from 'canvas';
import { Texture } from './Texture';
export class CanvasUtilities
{
public static cropTransparentPixels(canvas: Canvas): Canvas
{
const bounds = {
top: null,
left: null,
right: null,
bottom: null
};
const ctx = canvas.getContext('2d');
const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
const length = pixels.data.length;
for(let i = 0; i < length; i += 4)
{
if(pixels.data[i+3] !== 0)
{
const x = (i / 4) % canvas.width;
const y = ~~((i / 4) / canvas.width);
if(bounds.top === null) bounds.top = y;
if(bounds.left === null) bounds.left = x;
else if (x < bounds.left) bounds.left = x;
if(bounds.right === null) bounds.right = x;
else if(bounds.right < x) bounds.right = x;
if(bounds.bottom === null) bounds.bottom = y;
else if (bounds.bottom < y) bounds.bottom = y;
}
}
const trimHeight = (bounds.bottom - bounds.top);
const trimWidth = (bounds.right - bounds.left);
const trimmed = ctx.getImageData(bounds.left, bounds.top, trimWidth, trimHeight);
canvas.width = trimWidth;
canvas.height = trimHeight;
ctx.putImageData(trimmed, 0, 0);
return canvas;
}
public static tintWithMultiply(texture: Texture, color: number): Canvas
{
const crop = texture.frame.clone();
const resolution = texture.baseTexture.resolution;
const canvas = createCanvas(Math.ceil(crop.width), Math.ceil(crop.height));
const ctx = canvas.getContext('2d');
crop.x *= resolution;
crop.y *= resolution;
crop.width *= resolution;
crop.height *= resolution;
ctx.save();
ctx.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`;
ctx.fillRect(0, 0, crop.width, crop.height);
ctx.globalCompositeOperation = 'multiply';
const source = texture.baseTexture.getDrawableSource();
ctx.drawImage(
source,
crop.x,
crop.y,
crop.width,
crop.height,
0,
0,
crop.width,
crop.height
);
ctx.globalCompositeOperation = 'destination-atop';
ctx.drawImage(
source,
crop.x,
crop.y,
crop.width,
crop.height,
0,
0,
crop.width,
crop.height
);
ctx.restore();
return canvas;
}
}

160
src/core/utils/Texture.ts Normal file
View File

@ -0,0 +1,160 @@
import { Canvas, createCanvas } from 'canvas';
import { BaseTexture } from './BaseTexture';
import { Rectangle } from './Rectangle';
export class Texture
{
private _baseTexture: BaseTexture;
private _frame: Rectangle;
private _orig: Rectangle;
private _trim: Rectangle;
private _drawableCanvas: Canvas;
constructor(baseTexture: BaseTexture, frame: Rectangle, orig: Rectangle, trim: Rectangle)
{
this._baseTexture = baseTexture;
this._frame = frame;
this._orig = orig;
this._trim = trim;
this.buildTexture();
}
private buildTexture(): void
{
const width = this._frame.width;
const height = this._frame.height;
let dx = 0;
let dy = 0;
if(this._trim)
{
dx = (this._trim.width / 2) + this._trim.x - (0 * this._orig.width);
dy = (this._trim.height / 2) + this._trim.y - (0 * this._orig.height);
}
else
{
dx = (0.5 - 0) * this._orig.width;
dy = (0.5 - 0) * this._orig.height;
}
dx -= width / 2;
dy -= height / 2;
const canvas = createCanvas(this._orig.width, this._orig.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(
this._baseTexture.image,
this._frame.x * this._baseTexture.resolution,
this._frame.y * this._baseTexture.resolution,
Math.floor(width * this._baseTexture.resolution),
Math.floor(height * this._baseTexture.resolution),
Math.floor(dx * this._baseTexture.resolution),
Math.floor(dy * this._baseTexture.resolution),
Math.floor(width * this._baseTexture.resolution),
Math.floor(height * this._baseTexture.resolution)
);
this._drawableCanvas = canvas;
}
public getTintedWithMultiply(color: number): Canvas
{
const width = this._frame.width;
const height = this._frame.height;
let dx = 0;
let dy = 0;
if(this._trim)
{
dx = (this._trim.width / 2) + this._trim.x - (0 * this._orig.width);
dy = (this._trim.height / 2) + this._trim.y - (0 * this._orig.height);
}
else
{
dx = (0.5 - 0) * this._orig.width;
dy = (0.5 - 0) * this._orig.height;
}
dx -= width / 2;
dy -= height / 2;
const canvas = createCanvas(this._orig.width, this._orig.height);
const ctx = canvas.getContext('2d');
ctx.save();
ctx.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`;
ctx.fillRect(0, 0, this._orig.width, this._orig.height);
ctx.globalCompositeOperation = 'multiply';
ctx.drawImage(
this._baseTexture.image,
this._frame.x * this._baseTexture.resolution,
this._frame.y * this._baseTexture.resolution,
Math.floor(width * this._baseTexture.resolution),
Math.floor(height * this._baseTexture.resolution),
Math.floor(dx * this._baseTexture.resolution),
Math.floor(dy * this._baseTexture.resolution),
Math.floor(width * this._baseTexture.resolution),
Math.floor(height * this._baseTexture.resolution)
);
ctx.globalCompositeOperation = 'destination-atop';
ctx.drawImage(
this._baseTexture.image,
this._frame.x * this._baseTexture.resolution,
this._frame.y * this._baseTexture.resolution,
Math.floor(width * this._baseTexture.resolution),
Math.floor(height * this._baseTexture.resolution),
Math.floor(dx * this._baseTexture.resolution),
Math.floor(dy * this._baseTexture.resolution),
Math.floor(width * this._baseTexture.resolution),
Math.floor(height * this._baseTexture.resolution)
);
ctx.restore();
return canvas;
}
public get baseTexture(): BaseTexture
{
return this._baseTexture;
}
public get frame(): Rectangle
{
return this._frame;
}
public get orig(): Rectangle
{
return this._orig;
}
public get trim(): Rectangle
{
return this._trim;
}
public get drawableCanvas(): Canvas
{
return this._drawableCanvas;
}
public get width(): number
{
return this.drawableCanvas.width;
}
public get height(): number
{
return this._drawableCanvas.height;
}
}

View File

@ -1,6 +1,9 @@
export * from './AdvancedMap'; export * from './AdvancedMap';
export * from './BaseTexture';
export * from './BinaryReader'; export * from './BinaryReader';
export * from './CanvasUtilities';
export * from './File'; export * from './File';
export * from './FileUtilities'; export * from './FileUtilities';
export * from './Point'; export * from './Point';
export * from './Rectangle'; export * from './Rectangle';
export * from './Texture';