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--;
}
//CanvasUtilities.cropTransparentPixels(canvas);
//if(this._avatarSpriteData && this._avatarSpriteData.paletteIsGrayscale) this.convertToGrayscale(container);
return canvas;

View File

@ -12,6 +12,7 @@ import { AvatarCanvas } from '../structure';
import { AvatarImageActionCache } from './AvatarImageActionCache';
import { AvatarImageBodyPartCache } from './AvatarImageBodyPartCache';
import { AvatarImageDirectionCache } from './AvatarImageDirectionCache';
import { CompleteImageData } from './CompleteImageData';
import { ImageData } from './ImageData';
export class AvatarImageCache
@ -416,7 +417,7 @@ export class AvatarImageCache
imageIndex--;
}
return new AvatarImageBodyPartContainer(imageData.texture, offset, isCacheable);
return new AvatarImageBodyPartContainer(imageData.image, offset, isCacheable);
}
private convertColorToHex(k: number): string
@ -429,7 +430,7 @@ export class AvatarImageCache
return _local_2;
}
private createUnionImage(imageDatas: ImageData[], isFlipped: boolean): ImageData
private createUnionImage(imageDatas: ImageData[], isFlipped: boolean): CompleteImageData
{
const bounds = new Rectangle();
@ -470,10 +471,11 @@ export class AvatarImageCache
ty = (regPoint.y - data.rect.y);
}
const tintedTexture = texture.getTintedWithMultiply(color);
ctx.save();
//ctx.fillStyle = ('#' + this.convertColorToHex(data.colorTransform));
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();
// set the color
@ -481,6 +483,6 @@ export class AvatarImageCache
//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 } from '../../../core';
import { Point, Rectangle, Texture } from '../../../core';
export class ImageData
{
private _texture: Canvas;
private _texture: Texture;
private _rect: Rectangle;
private _regPoint: Point;
private _flipH: boolean;
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._rect = rectangle;
@ -27,7 +26,7 @@ export class ImageData
this._colorTransform = null;
}
public get texture(): Canvas
public get texture(): Texture
{
return this._texture;
}

View File

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

View File

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

View File

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

View File

@ -1,5 +1,4 @@
import { Canvas } from 'canvas';
import { Rectangle } from '../../utils';
import { Rectangle, Texture } from '../../utils';
import { IGraphicAsset } from './IGraphicAsset';
export class GraphicAsset implements IGraphicAsset
@ -8,7 +7,7 @@ export class GraphicAsset implements IGraphicAsset
private _name: string;
private _source: string;
private _texture: Canvas;
private _texture: Texture;
private _usesPalette: boolean;
private _x: number;
private _y: number;
@ -19,7 +18,7 @@ export class GraphicAsset implements IGraphicAsset
private _rectangle: Rectangle;
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());
@ -74,7 +73,7 @@ export class GraphicAsset implements IGraphicAsset
return this._source;
}
public get texture(): Canvas
public get texture(): Texture
{
return this._texture;
}

View File

@ -1,5 +1,5 @@
import { Canvas, createCanvas, Image } from 'canvas';
import { AdvancedMap, Rectangle } from '../../utils';
import { Image } from 'canvas';
import { AdvancedMap, BaseTexture, Rectangle, Texture } from '../../utils';
import { IAsset, IAssetData, IAssetPalette } from '../interfaces';
import { GraphicAsset } from './GraphicAsset';
import { GraphicAssetPalette } from './GraphicAssetPalette';
@ -12,12 +12,12 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
private _name: string;
private _data: IAssetData;
private _textures: AdvancedMap<string, Canvas>;
private _textures: AdvancedMap<string, Texture>;
private _assets: AdvancedMap<string, GraphicAsset>;
private _palettes: AdvancedMap<string, GraphicAssetPalette>;
private _paletteAssetNames: string[];
constructor(data: IAssetData, baseTexture: Image)
constructor(data: IAssetData, image: Image)
{
if(!data) throw new Error('invalid_collection');
@ -28,7 +28,12 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
this._palettes = new AdvancedMap();
this._paletteAssetNames = [];
if(baseTexture) this.processBaseTexture(baseTexture);
if(image)
{
const baseTexture = new BaseTexture(image);
this.processBaseTexture(baseTexture);
}
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;
@ -107,58 +112,9 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
);
}
const width = frame.width;
const height = frame.height;
const texture = new Texture(baseTexture, frame, orig, trim);
let dx = 0;
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);
this._textures.add(name, texture);
}
}
@ -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;
@ -253,7 +209,7 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
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);
@ -350,7 +306,7 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
existing.recycle();
}
public getLibraryAsset(name: string): Canvas
public getLibraryAsset(name: string): Texture
{
if(!name) return null;
@ -386,7 +342,7 @@ export class GraphicAssetCollection implements IGraphicAssetCollection
return this._data;
}
public get textures(): AdvancedMap<string, Canvas>
public get textures(): AdvancedMap<string, Texture>
{
return this._textures;
}

View File

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

View File

@ -1,11 +1,10 @@
import { Canvas } from 'canvas';
import { Rectangle } from '../../utils';
import { Rectangle, Texture } from '../../utils';
export interface IGraphicAsset
{
name: string;
source: string;
texture: Canvas;
texture: Texture;
usesPalette: boolean;
x: 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 './BaseTexture';
export * from './BinaryReader';
export * from './CanvasUtilities';
export * from './File';
export * from './FileUtilities';
export * from './Point';
export * from './Rectangle';
export * from './Texture';