Skip to content

Commit

Permalink
Dedicated class for manipulating images before they are displayed
Browse files Browse the repository at this point in the history
  • Loading branch information
AntumDeluge committed Jun 1, 2024
1 parent 85d6cff commit c482f6d
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 14 deletions.
5 changes: 5 additions & 0 deletions src/js/stendhal/SingletonRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { WeatherRenderer } from "./util/WeatherRenderer";

import { CStatus } from "./data/CStatus";
import { CacheManager } from "./data/CacheManager";
import { DrawingStage } from "./data/DrawingStage";
import { EmojiStore } from "./data/EmojiStore";
import { GroupManager } from "./data/GroupManager";
import { Map } from "./data/Map";
Expand Down Expand Up @@ -74,6 +75,10 @@ export class SingletonRepo {
return DownloadUtil;
}

static getDrawingStage(): DrawingStage {
return DrawingStage.get();
}

static getEmojiStore(): EmojiStore {
return EmojiStore.get();
}
Expand Down
139 changes: 139 additions & 0 deletions src/js/stendhal/data/DrawingStage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/***************************************************************************
* Copyright © 2024 - Faiumoni e. V. *
***************************************************************************
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation; either version 3 of the *
* License, or (at your option) any later version. *
* *
***************************************************************************/


/**
* Hidden canvas for manipulating images before they are displayed.
*/
export class DrawingStage {

private canvas: HTMLCanvasElement;

/** Singleton instance. */
private static instance: DrawingStage;


/**
* Retrieves the singleton instance.
*/
public static get(): DrawingStage {
if (!DrawingStage.instance) {
DrawingStage.instance = new DrawingStage();
}
return DrawingStage.instance;
}

/**
* Hidden singleton constructor.
*/
private constructor() {
this.canvas = document.getElementById("drawing-stage")! as HTMLCanvasElement;
// initialize with smallest possible dimensions
this.clear();
}

/**
* Retreives the 2D drawing context of the canves.
*/
private getContext(): CanvasRenderingContext2D {
return this.canvas.getContext("2d")! as CanvasRenderingContext2D;
}

/**
* Clears image data from canvas and sets size to minimum possible dimensions.
*/
public clear() {
this.getContext().clearRect(0, 0, this.canvas.width, this.canvas.height);
this.canvas.width = 0;
this.canvas.height = 0;
}

/**
* Sets canvas dimensions.
*/
private setSize(width: number, height: number) {
this.canvas.width = width;
this.canvas.height = height;
}

/**
* Compares canvas dimensions against image and adjusts if necessary.
*/
private checkSize(image: HTMLImageElement) {
if (this.canvas.width < image.width || this.canvas.height < image.height) {
this.setSize(image.width, image.height);
}
}

/**
* Draws an image on the canvas.
*
* @param image
* Image to be drawn.
* @param clear
* If `true` erases current image data before drawing.
*/
public drawImage(image: HTMLImageElement, clear=true) {
if (clear) {
this.clear();
}
this.checkSize(image);
this.getContext().drawImage(image, 0, 0);
}

/**
* Draws a rotated image on the canvas.
*
* NOTE: currently only supports accurate rotation on square image in 90 degree increments
*
* @param image
* Image to be drawn.
* @param angle
* Desired angle of image rotation.
* @param clear
* If `true` erases current image data before drawing.
*/
public drawImageRotated(image: HTMLImageElement, angle: number, clear=true) {
if (clear) {
this.clear();
}
this.checkSize(image);
const ctx = this.getContext();
ctx.translate(this.canvas.width / 2, this.canvas.height / 2);
ctx.rotate(angle * Math.PI / 180);
ctx.translate(-this.canvas.width / 2, -this.canvas.height / 2);
// NOTE: do we need to set canvas size again in case of non-square image?
ctx.drawImage(image, 0, 0);
}

/**
* Converts canvas data to data URL.
*
* @return
* String representation of image data.
*/
public toDataURL(): string {
return this.canvas.toDataURL("image/png");
}

/**
* Converts canvas to PNG image.
*
* @return
* New image.
*/
public toImage(): HTMLImageElement {
const image = new Image();
image.src = this.canvas.toDataURL("image/png");
return image;
}
}
19 changes: 5 additions & 14 deletions src/js/stendhal/data/SpriteStore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/***************************************************************************
* (C) Copyright 2003-2023 - Stendhal *
* (C) Copyright 2003-2024 - Stendhal *
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
Expand All @@ -11,6 +11,7 @@

declare var stendhal: any;

import { DrawingStage } from "./DrawingStage";
import { Paths } from "./Paths";


Expand Down Expand Up @@ -135,19 +136,9 @@ export class SpriteStore {
* Angle of rotation.
*/
private rotate(img: HTMLImageElement, angle: number) {
const canvas = <HTMLCanvasElement> document.getElementById("drawing-stage")!;
const ctx = canvas.getContext("2d")!;
// make sure working with blank canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = img.width;
canvas.height = img.height;

ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(angle * Math.PI / 180);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.drawImage(img, 0, 0);

img.src = canvas.toDataURL("image/png");
const stage = DrawingStage.get();
stage.drawImageRotated(img, angle);
img.src = stage.toDataURL();
}

/**
Expand Down

0 comments on commit c482f6d

Please sign in to comment.