From 7e9974bde85eb86109586c452a57356ccc969121 Mon Sep 17 00:00:00 2001 From: Nicholas Furgiuele Date: Tue, 3 Oct 2023 15:58:54 +1100 Subject: [PATCH 1/8] Add tile map menu. --- wwwroot/modules/ui/tileManager.html | 21 ++++++++++++++++++--- wwwroot/modules/ui/tileManager.js | 4 ++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/wwwroot/modules/ui/tileManager.html b/wwwroot/modules/ui/tileManager.html index 6ed71f3..2c80eea 100644 --- a/wwwroot/modules/ui/tileManager.html +++ b/wwwroot/modules/ui/tileManager.html @@ -50,16 +50,31 @@
-
+
+ +
diff --git a/wwwroot/modules/ui/tileManager.js b/wwwroot/modules/ui/tileManager.js index 163962c..50f5acc 100644 --- a/wwwroot/modules/ui/tileManager.js +++ b/wwwroot/modules/ui/tileManager.js @@ -20,9 +20,13 @@ const commands = { tileMapSelect: 'tileMapSelect', tileMapDelete: 'tileMapDelete', tileMapClone: 'tileMapClone', + tileMapCloneMirror: 'tileMapCloneMirror', + tileMapMirror: 'tileMapMirror', + tileMapFlip: 'tileMapFlip', tileMapChangePosition: 'tileMapChangePosition', tileSelect: 'tileSelect', tileMapChange: 'tileMapChange', + tileMapReference: 'tileMapReference', tileSetChange: 'tileSetChange', tileSetToTileMap: 'tileSetToTileMap', assemblyImport: 'assemblyImport', From e82d4286529ad82e8348075d55ba35aab70fba70 Mon Sep 17 00:00:00 2001 From: Nicholas Furgiuele Date: Tue, 3 Oct 2023 17:02:21 +1100 Subject: [PATCH 2/8] Tile map mirror and flip: - Added mirror and flip functions. - Implemented mirror and clip in UI. --- wwwroot/modules/main.js | 100 +++++++++++++++++++++++++--- wwwroot/modules/ui/tileManager.js | 35 +++++----- wwwroot/modules/util/tileMapUtil.js | 38 +++++++++++ 3 files changed, 146 insertions(+), 27 deletions(-) diff --git a/wwwroot/modules/main.js b/wwwroot/modules/main.js index 3551f36..1c20da9 100644 --- a/wwwroot/modules/main.js +++ b/wwwroot/modules/main.js @@ -66,6 +66,7 @@ import KeyboardManager, { KeyDownHandler, KeyUpHandler } from "./components/keyb import ProjectList from "./models/projectList.js"; import SystemUtil from "./util/systemUtil.js"; import { DropPosition } from "./types.js"; +import TileMapUtil from "./util/tileMapUtil.js"; /* **************************************************************************************************** @@ -1383,6 +1384,10 @@ function handleTileManagerOnCommand(args) { tileMapClone(args.tileMapId); break; + case TileManager.Commands.tileMapCloneMirror: + tileMapClone(args.tileMapId, { mirror: true }); + break; + case TileManager.Commands.tileMapDelete: tileMapRemove(args.tileMapId); break; @@ -1391,6 +1396,18 @@ function handleTileManagerOnCommand(args) { tileMapUpdate(args.tileMapId, args); break; + case TileManager.Commands.tileMapMirror: + tileMapMirrorOrFlip(args.tileMapId, { mirror: true }); + break; + + case TileManager.Commands.tileMapFlip: + tileMapMirrorOrFlip(args.tileMapId, { flip: true }); + break; + + case TileManager.Commands.tileMapReference: + referenceImageFromTileMap(args.tileMapId); + break; + case TileManager.Commands.tileSetChange: tileSetUpdate(args); break; @@ -3313,6 +3330,18 @@ function selectReferenceImage() { fileInput.click(); } +/** + * Sets a tile map as a reference image. + * @param {string} tileMapId - Unique ID of the tile map. + */ +function referenceImageFromTileMap(tileMapId) { + // if (!tileMapId || typeof tileMapId !== 'string') throw new Error('Please pass a tile map ID.'); + // const tileMap = getTileMapList().getTileMapById(tileMapId); + // if (tileMap) throw new Error('Tile map not found.'); + + // const tileMapImage = ImageUtil. +} + function clearReferenceImage() { instanceState.referenceImage.clearImage(); @@ -4554,7 +4583,7 @@ function tileMapReorder(tileMapId, targetTileMapId, position) { state.saveToLocalStorage(); - tileManager.setState({ + tileManager.setState({ tileMapList: getTileMapList() }); } @@ -4643,8 +4672,9 @@ function tileSetUpdate(args) { /** * Clones a tile map. * @param {string} tileMapId - Unique ID of the tile map to delete. + * @param {{ mirror: boolean, flip: boolean }} args */ -function tileMapClone(tileMapId) { +function tileMapClone(tileMapId, args) { if (!tileMapId) throw new Error('The tile map ID was invalid.'); const sourceTileMap = getTileMapList().getTileMapById(tileMapId); @@ -4653,14 +4683,28 @@ function tileMapClone(tileMapId) { addUndoState(); - const clonedTileMap = TileMapFactory.clone(sourceTileMap); - clonedTileMap.title += " (copy)"; + try { + + const clonedTileMap = TileMapFactory.clone(sourceTileMap); + clonedTileMap.title += " (copy)"; + + if (args?.mirror === true) { + TileMapUtil.mirrorTileMap(clonedTileMap); + } + + if (args?.flip === true) { + TileMapUtil.flipTileMap(clonedTileMap); + } + + getTileMapList().addTileMap(clonedTileMap); - getTileMapList().addTileMap(clonedTileMap); + tileMapOrTileSetSelectById(clonedTileMap.tileMapId); - tileMapOrTileSetSelectById(clonedTileMap.tileMapId); + toast.show('Tile map cloned.'); - toast.show('Tile map cloned.'); + } catch (e) { + + } } /** @@ -4737,7 +4781,7 @@ function tileMapUpdate(tileMapId, args) { const capabilityType = isTileMap() ? getTileMap().isSprite ? 'sprite' : 'background' : 'background'; const graphicsCapability = SystemUtil.getGraphicsCapability(getProject().systemType, capabilityType); - // Reset UI + // Update UI tileManager.setState({ tileMapList: getTileMapList(), selectedTileMapId: getProjectUIState().tileMapId, @@ -4755,6 +4799,46 @@ function tileMapUpdate(tileMapId, args) { }); } +/** + * @param {string} tileMapId + * @param {{ mirror: boolean, flip: boolean }} args + */ +function tileMapMirrorOrFlip(tileMapId, args) { + if (!tileMapId) throw new Error('The tile map ID was invalid.'); + const tileMap = getTileMapList().getTileMapById(tileMapId); + if (!tileMap) throw new Error('No tile map matched the given ID.'); + + addUndoState(); + + try { + + if (args?.mirror === true) { + TileMapUtil.mirrorTileMap(tileMap); + toast.show('Tile map mirrored.'); + } + + if (args?.flip === true) { + TileMapUtil.flipTileMap(tileMap); + toast.show('Tile map flipped.'); + } + + } catch (e) { + undoManager.removeLastUndo(); + toast.show('Error mirroring or flipping tile map.') + throw e; + } + + // Update project + state.saveToLocalStorage(); + + // Update UI + tileEditor.setState({ + selectedTileIndex: -1, + tileGrid: getTileGrid(), + forceRefresh: true + }); +} + /** * @param {string?} field */ diff --git a/wwwroot/modules/ui/tileManager.js b/wwwroot/modules/ui/tileManager.js index 50f5acc..a4e739f 100644 --- a/wwwroot/modules/ui/tileManager.js +++ b/wwwroot/modules/ui/tileManager.js @@ -129,19 +129,6 @@ export default class TileManager extends ComponentBase { this.#dispatcher = new EventDispatcher(); TemplateUtil.wireUpLabels(this.#element); - TemplateUtil.wireUpCommandAutoEvents(this.#element, (sender, ev, command, event) => { - // Tile set select - if (command === commands.tileSetSelect) { - const args = this.#createArgs(TileManager.Commands.tileSetSelect); - this.#dispatcher.dispatch(EVENT_OnCommand, args); - } - // Sort - else if (command === commands.sort) { - const args = this.#createArgs(TileManager.Commands.sort); - args.field = sender.getAttribute('data-field'); - this.#dispatcher.dispatch(EVENT_OnCommand, args); - } - }); this.#reorderHelper = new StackedListReorderHelper( this.#element.querySelector('[data-smsgfx-id=tile-map-list-container]'), @@ -395,13 +382,23 @@ export default class TileManager extends ComponentBase { * @param {HTMLElement} parentElement */ #wireAutoEvents(parentElement) { - parentElement.querySelectorAll('[data-command][data-auto-event]').forEach((element) => { - const event = element.getAttribute('data-auto-event'); - element.addEventListener(event, () => { - const command = element.getAttribute('data-command'); - const args = this.#createArgs(command, element); + TemplateUtil.wireUpCommandAutoEvents(parentElement, (sender, ev, command, event) => { + if (command === commands.tileSetSelect) { + // Tile set select + const args = this.#createArgs(TileManager.Commands.tileSetSelect); this.#dispatcher.dispatch(EVENT_OnCommand, args); - }); + } + else if (command === commands.sort) { + // Sort + const args = this.#createArgs(TileManager.Commands.sort); + args.field = sender.getAttribute('data-field'); + this.#dispatcher.dispatch(EVENT_OnCommand, args); + } else { + // Generic + const args = this.#createArgs(command, sender); + args.tileMapId = this.#selectedTileMapId; + this.#dispatcher.dispatch(EVENT_OnCommand, args); + } }); } diff --git a/wwwroot/modules/util/tileMapUtil.js b/wwwroot/modules/util/tileMapUtil.js index 28258e5..9f3f975 100644 --- a/wwwroot/modules/util/tileMapUtil.js +++ b/wwwroot/modules/util/tileMapUtil.js @@ -112,6 +112,44 @@ export default class TileMapUtil { }; } + /** + * Mirrors a tile map horizontally. + * @param {TileMap} tileMap - The tile map to mirror horizontally. + */ + static mirrorTileMap(tileMap) { + if (!tileMap instanceof TileMap) throw new Error('Tile map was not passed.'); + + for (let rowNum = 0; rowNum < tileMap.rowCount; rowNum++) { + const tilesInRow = tileMap.getTileMapRow(rowNum); + tilesInRow.reverse(); + for (let colNum = 0; colNum < tileMap.columnCount; colNum++) { + const thisTile = tilesInRow[colNum]; + thisTile.horizontalFlip = !thisTile.horizontalFlip; + const tileIndex = (rowNum * tileMap.columnCount) + colNum; + tileMap.setTileByIndex(tileIndex, thisTile); + } + } + } + + /** + * Flips a tile map vertically. + * @param {TileMap} tileMap - The tile map to flip vertically. + */ + static flipTileMap(tileMap) { + if (!tileMap instanceof TileMap) throw new Error('Tile map was not passed.'); + + for (let colNum = 0; colNum < tileMap.columnCount; colNum++) { + const tilesInCol = tileMap.getTileMapColumn(colNum); + tilesInCol.reverse(); + for (let rowNum = 0; rowNum < tileMap.rowCount; rowNum++) { + const thisTile = tilesInCol[rowNum]; + thisTile.verticalFlip = !thisTile.verticalFlip; + const tileIndex = (rowNum * tileMap.columnCount) + colNum; + tileMap.setTileByIndex(tileIndex, thisTile); + } + } + } + } From ff36c52d686cdb43f609a65ee91609709988e17f Mon Sep 17 00:00:00 2001 From: Nicholas Furgiuele Date: Sun, 5 Nov 2023 10:40:27 +1100 Subject: [PATCH 3/8] Commit changes. --- wwwroot/modules/main.js | 3 ++- wwwroot/modules/models/referenceImage.js | 2 +- wwwroot/modules/util/imageUtil.js | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/wwwroot/modules/main.js b/wwwroot/modules/main.js index 1c20da9..789d310 100644 --- a/wwwroot/modules/main.js +++ b/wwwroot/modules/main.js @@ -67,6 +67,7 @@ import ProjectList from "./models/projectList.js"; import SystemUtil from "./util/systemUtil.js"; import { DropPosition } from "./types.js"; import TileMapUtil from "./util/tileMapUtil.js"; +import PaintUtil from "./util/paintUtil.js"; /* **************************************************************************************************** @@ -3339,7 +3340,7 @@ function referenceImageFromTileMap(tileMapId) { // const tileMap = getTileMapList().getTileMapById(tileMapId); // if (tileMap) throw new Error('Tile map not found.'); - // const tileMapImage = ImageUtil. + // const tileMapImage = PaintUtil. } function clearReferenceImage() { diff --git a/wwwroot/modules/models/referenceImage.js b/wwwroot/modules/models/referenceImage.js index 17e46d1..fafae9a 100644 --- a/wwwroot/modules/models/referenceImage.js +++ b/wwwroot/modules/models/referenceImage.js @@ -59,7 +59,7 @@ export default class ReferenceImage { if (image instanceof ImageBitmap) { this.#image = image; } else if ((image?.tagName && image.tagName === 'IMG') || (image?.tagName && image.tagName === 'CANVAS') || image instanceof OffscreenCanvas) { - this.#image = ImageUtil.ToImageBitmap(image); + this.#image = ImageUtil.toImageBitmap(image); } else if (image === null) { this.#image = null; } else { diff --git a/wwwroot/modules/util/imageUtil.js b/wwwroot/modules/util/imageUtil.js index 8751f9f..ddffa5b 100644 --- a/wwwroot/modules/util/imageUtil.js +++ b/wwwroot/modules/util/imageUtil.js @@ -5,19 +5,19 @@ import ProjectFactory from "../factory/projectFactory.js"; import ColourUtil from "./colourUtil.js"; import TileSetFactory from "../factory/tileSetFactory.js"; import PaletteListFactory from "../factory/paletteListFactory.js"; -import GeneralUtil from "./generalUtil.js"; +import TileMap from "../models/tileMap.js"; const imageMimeTypes = ['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/svg+xml']; export default class ImageUtil { - + /** * Converts an image to an image bitmap. * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas} image - Image to convert. * @returns {ImageBitmap} */ - static ToImageBitmap(image) { + static toImageBitmap(image) { if (image instanceof ImageBitmap) { return image; } else if ((image.tagName && image.tagName === 'CANVAS') || image instanceof OffscreenCanvas) { From 6ee1f30aeefaea0939759cf3353b22997ba40522 Mon Sep 17 00:00:00 2001 From: Nicholas Furgiuele Date: Mon, 6 Nov 2023 03:06:09 +1100 Subject: [PATCH 4/8] Progress. --- wwwroot/modules/main.js | 12 ++++++++---- wwwroot/modules/util/paintUtil.js | 30 +++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/wwwroot/modules/main.js b/wwwroot/modules/main.js index 789d310..50a83ec 100644 --- a/wwwroot/modules/main.js +++ b/wwwroot/modules/main.js @@ -3336,11 +3336,15 @@ function selectReferenceImage() { * @param {string} tileMapId - Unique ID of the tile map. */ function referenceImageFromTileMap(tileMapId) { - // if (!tileMapId || typeof tileMapId !== 'string') throw new Error('Please pass a tile map ID.'); - // const tileMap = getTileMapList().getTileMapById(tileMapId); - // if (tileMap) throw new Error('Tile map not found.'); + if (!tileMapId || typeof tileMapId !== 'string') throw new Error('Please pass a tile map ID.'); + const tileMap = getTileMapList().getTileMapById(tileMapId); + if (!tileMap) throw new Error('Tile map not found.'); + + console.log('Yes the tile map was found.'); - // const tileMapImage = PaintUtil. + const canv = new OffscreenCanvas(tileMap.columnCount * 8, tileMap.rowCount * 8); + const tileMapImage = PaintUtil.drawTileImage(canv.getContext('2d'), 0, 0, canv.width, canv.height, ) + const tileMapImage = PaintUtil.dr(canv.getContext('2d'), 0, 0, canv.width, canv.height, ) } function clearReferenceImage() { diff --git a/wwwroot/modules/util/paintUtil.js b/wwwroot/modules/util/paintUtil.js index ae75c6b..b8f7266 100644 --- a/wwwroot/modules/util/paintUtil.js +++ b/wwwroot/modules/util/paintUtil.js @@ -4,6 +4,8 @@ import Tile from '../models/tile.js'; import TileSet from '../models/tileSet.js' import TileGridProvider from '../models/tileGridProvider.js'; import TileSetFactory from '../factory/tileSetFactory.js'; +import TileMap from '../models/tileMap.js'; +import PaletteList from '../models/paletteList.js'; export default class PaintUtil { @@ -226,6 +228,32 @@ export default class PaintUtil { } + /** + * Draws a tile map onto a canvas object. + * @param {HTMLCanvasElement|OffscreenCanvas} canvas - Canvas to draw onto. + * @param {TileGridProvider} tileGrid - Tile grid to draw. + * @param {PaletteList} paletteList - Palettes to use when drawing. + */ + static drawTileGridOntoCanvas(canvas, tileGrid, paletteList) { + if (!canvas instanceof HTMLCanvasElement && !canvas instanceof OffscreenCanvas) + throw new Error('Non valid canvas object was passed.'); + if (!tileGrid instanceof TileGridProvider) + throw new Error('Tile grid was not valid.'); + if (!paletteList instanceof PaletteList) + throw new Error('Palette list was not valid.'); + + const ctx = canvas.getContext('2d'); + + for (let col = 0; col < tileGrid.columnCount; col++) { + for (let row = 0; row < tileGrid.rowCount; row++) { + let tile = tileGrid.getTileInfoByRowAndColumn(row, col); + if (tile) { + + } + } + } + } + /** * Draw a single tile to a canvas. * @param {HTMLCanvasElement} canvas - Canvas element to draw onto. @@ -365,7 +393,7 @@ export default class PaintUtil { canvasX += hFlip ? -scaleX : scaleX; } canvasY += vflip ? -scaleY : scaleY; - canvasX = hFlip ? x + (scaleX * 7): x; + canvasX = hFlip ? x + (scaleX * 7) : x; } } From 3b66aeca4ba06ddff5905bdd05525eb8da4714c5 Mon Sep 17 00:00:00 2001 From: Nicholas Furgiuele Date: Tue, 7 Nov 2023 13:35:56 +1100 Subject: [PATCH 5/8] Check in, because going for a bike ride. --- wwwroot/modules/models/tileGridProvider.js | 10 + wwwroot/modules/util/paintUtil.js | 205 ++++++++++----------- 2 files changed, 108 insertions(+), 107 deletions(-) diff --git a/wwwroot/modules/models/tileGridProvider.js b/wwwroot/modules/models/tileGridProvider.js index 15dfd6d..f08f377 100644 --- a/wwwroot/modules/models/tileGridProvider.js +++ b/wwwroot/modules/models/tileGridProvider.js @@ -45,6 +45,16 @@ export default class TileGridProvider { } + /** + * Gets information about a tile by the tile index. + * @param {number} tileIndex - Index of the tile. + * @returns {TileProviderTileInfo} + */ + getTileInfoByIndex(tileIndex) { + throw new Error('Not implemented.'); + } + + /** * Gets information about a tile by the tile index. * @param {number} tileIndex - Index of the tile. diff --git a/wwwroot/modules/util/paintUtil.js b/wwwroot/modules/util/paintUtil.js index b8f7266..0f48c43 100644 --- a/wwwroot/modules/util/paintUtil.js +++ b/wwwroot/modules/util/paintUtil.js @@ -230,63 +230,18 @@ export default class PaintUtil { /** * Draws a tile map onto a canvas object. - * @param {HTMLCanvasElement|OffscreenCanvas} canvas - Canvas to draw onto. * @param {TileGridProvider} tileGrid - Tile grid to draw. + * @param {TileSet} tileSet - Tile set that contains the tiles used by the tile grid. * @param {PaletteList} paletteList - Palettes to use when drawing. + * @param {number[]?} [paletteIndiciesToRenderTransparent] - Palette indicies to render as transparent. + * @returns {ImageBitmap} */ - static drawTileGridOntoCanvas(canvas, tileGrid, paletteList) { - if (!canvas instanceof HTMLCanvasElement && !canvas instanceof OffscreenCanvas) - throw new Error('Non valid canvas object was passed.'); - if (!tileGrid instanceof TileGridProvider) - throw new Error('Tile grid was not valid.'); - if (!paletteList instanceof PaletteList) - throw new Error('Palette list was not valid.'); - - const ctx = canvas.getContext('2d'); - - for (let col = 0; col < tileGrid.columnCount; col++) { - for (let row = 0; row < tileGrid.rowCount; row++) { - let tile = tileGrid.getTileInfoByRowAndColumn(row, col); - if (tile) { - - } - } - } - } - - /** - * Draw a single tile to a canvas. - * @param {HTMLCanvasElement} canvas - Canvas element to draw onto. - * @param {Tile} tile - Tile to draw. - * @param {Palette} palette - Palette to use to render the pixel. - */ - static drawTile(canvas, tile, palette) { - const context = canvas.getContext('2d'); - const numColours = palette.getColours().length; - - const w = canvas.width / 8; - const h = canvas.height / 8; - - for (let tilePx = 0; tilePx < 64; tilePx++) { - - const tileCol = tilePx % 8; - const tileRow = (tilePx - tileCol) / 8; - - const x = tileCol * w; - const y = tileRow * h; - - let pixelPaletteIndex = tile.readAt(tilePx); - - // Set colour - if (pixelPaletteIndex >= 0 && pixelPaletteIndex < numColours) { - const colour = palette.getColour(pixelPaletteIndex); - const hex = ColourUtil.toHex(colour.r, colour.g, colour.b); - context.fillStyle = hex; - } - - context.moveTo(0, 0); - context.fillRect(x, y, w, h); - } + static createTileGridImage(tileGrid, tileSet, paletteList, paletteIndiciesToRenderTransparent) { + const widthPx = tileGrid.columnCount * 8; + const heightPx = tileGrid.rowCount * 8; + const canvas = new OffscreenCanvas(widthPx, heightPx); + drawTileGridOntoCanvas(canvas, tileGrid, tileSet, paletteList, paletteIndiciesToRenderTransparent); + return canvas.transferToImageBitmap(); } /** @@ -344,59 +299,7 @@ export default class PaintUtil { } } - - /** - * Renders an image of the tile onto a canvas object, at the size of the canvas object. - * @param {CanvasRenderingContext2D} context - Context object to do the drawing. - * @param {number} x - X position to draw the tile image. - * @param {number} y - Y position to draw the tile image. - * @param {number} width - Width to draw the tile image. - * @param {number} height - Height to draw the tile image. - * @param {Tile} tile - Tile to draw to the canvas. - * @param {Palette} palette - Colour palette to use. - * @param {DrawTileImageOptions?} [options] - Optional. Additional options for painting the tile. - * @returns {ImageBitmap} - */ - static drawTileImage(context, x, y, width, height, tile, palette, options) { - let transIndexes = Array.isArray(options?.transparencyIndexes) ? options.transparencyIndexes : null; - let hFlip = options?.horizontalFlip === true; - let vflip = options?.verticalFlip === true; - - let tilePixelIndex = 0; - let scaleX = width / 8; - let scaleY = height / 8; - let canvasX = hFlip ? x + (scaleX * 7) : x; - let canvasY = vflip ? y + (scaleY * 7) : y; - - for (let tileY = 0; tileY < 8; tileY++) { - for (let tileX = 0; tileX < 8; tileX++) { - - const pixelPaletteIndex = tile.readAt(tilePixelIndex); - - // Set colour of the pixel - if (pixelPaletteIndex >= 0 && pixelPaletteIndex < palette.getColours().length) { - const colour = palette.getColour(pixelPaletteIndex); - context.fillStyle = `rgb(${colour.r}, ${colour.g}, ${colour.b})`; - } - - if (transIndexes && transIndexes.includes(pixelPaletteIndex)) { - // This palette index is within the transparency indexes, so it shouldn't be - // drawn, so clear it instead of drawing it. - context.clearRect(canvasX, canvasY, scaleX, scaleY); - } else { - // Pixel colour is not in transparency indicies, so draw it. - context.fillRect(canvasX, canvasY, scaleX, scaleY); - } - - tilePixelIndex++; - - canvasX += hFlip ? -scaleX : scaleX; - } - canvasY += vflip ? -scaleY : scaleY; - canvasX = hFlip ? x + (scaleX * 7) : x; - } - } - + static drawTileImage = drawTileImage; } @@ -490,6 +393,94 @@ function translateCoordinate(tileInfo, x, y) { }; } + +/** + * Draws a tile map onto a canvas object. + * @param {HTMLCanvasElement|OffscreenCanvas} canvas - Canvas to draw onto. + * @param {TileGridProvider} tileGrid - Tile grid to draw. + * @param {TileSet} tileSet - Tile set that contains the tiles used by the tile grid. + * @param {PaletteList} paletteList - Palettes to use when drawing. + */ +function drawTileGridOntoCanvas(canvas, tileGrid, tileSet, paletteList) { + if (!canvas instanceof HTMLCanvasElement && !canvas instanceof OffscreenCanvas) throw new Error('Non valid canvas object was passed.'); + if (!tileGrid instanceof TileGridProvider) throw new Error('Tile grid was not valid.'); + if (!tileSet instanceof TileSet) throw new Error('Tile set was not valid.'); + if (!paletteList instanceof PaletteList) throw new Error('Palette list was not valid.'); + + const context = canvas.getContext('2d'); + const dimensions = { w: 8, h: 8 }; + + for (let col = 0; col < tileGrid.columnCount; col++) { + for (let row = 0; row < tileGrid.rowCount; row++) { + const tileInfo = tileGrid.getTileInfoByRowAndColumn(row, col); + const tile = tileSet?.getTile(tileInfo.tileId) ?? null; + const palette = paletteList?.getPaletteByIndex(tileInfo.paletteIndex) ?? null; + const coords = { x: col * 8, y: row * 8 }; + if (tile && palette) { + drawTileImage(context, + coords.x, coords.y, + dimensions.w, dimensions.h, + tile, palette, + { horizontalFlip: tileInfo.horizontalFlip, verticalFlip: tileInfo.verticalFlip } + ); + } + } + } +} + +/** + * Renders an image of the tile onto a canvas object, at the size of the canvas object. + * @param {CanvasRenderingContext2D} context - Context object to do the drawing. + * @param {number} x - X position to draw the tile image. + * @param {number} y - Y position to draw the tile image. + * @param {number} width - Width to draw the tile image. + * @param {number} height - Height to draw the tile image. + * @param {Tile} tile - Tile to draw to the canvas. + * @param {Palette} palette - Colour palette to use. + * @param {DrawTileImageOptions?} [options] - Optional. Additional options for painting the tile. + * @returns {ImageBitmap} + */ +function drawTileImage(context, x, y, width, height, tile, palette, options) { + let transIndexes = Array.isArray(options?.transparencyIndexes) ? options.transparencyIndexes : null; + let hFlip = options?.horizontalFlip === true; + let vflip = options?.verticalFlip === true; + + let tilePixelIndex = 0; + let scaleX = width / 8; + let scaleY = height / 8; + let canvasX = hFlip ? x + (scaleX * 7) : x; + let canvasY = vflip ? y + (scaleY * 7) : y; + + for (let tileY = 0; tileY < 8; tileY++) { + for (let tileX = 0; tileX < 8; tileX++) { + + const pixelPaletteIndex = tile.readAt(tilePixelIndex); + + // Set colour of the pixel + if (pixelPaletteIndex >= 0 && pixelPaletteIndex < palette.getColours().length) { + const colour = palette.getColour(pixelPaletteIndex); + context.fillStyle = `rgb(${colour.r}, ${colour.g}, ${colour.b})`; + } + + if (transIndexes && transIndexes.includes(pixelPaletteIndex)) { + // This palette index is within the transparency indexes, so it shouldn't be + // drawn, so clear it instead of drawing it. + context.clearRect(canvasX, canvasY, scaleX, scaleY); + } else { + // Pixel colour is not in transparency indicies, so draw it. + context.fillRect(canvasX, canvasY, scaleX, scaleY); + } + + tilePixelIndex++; + + canvasX += hFlip ? -scaleX : scaleX; + } + canvasY += vflip ? -scaleY : scaleY; + canvasX = hFlip ? x + (scaleX * 7) : x; + } +} + + /** * @typedef {Object} FillProps * @property {TileGridProvider} tileGrid - Tile grid provider with tile layout. From db2ca84d1471ab80093958e744bd8c095bec225a Mon Sep 17 00:00:00 2001 From: nfaucode <150198926+nfaucode@users.noreply.github.com> Date: Thu, 9 Nov 2023 17:11:37 +1100 Subject: [PATCH 6/8] Train changes. --- wwwroot/modules/main.js | 19 +++++++++++++++---- wwwroot/modules/util/paintUtil.js | 15 ++++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/wwwroot/modules/main.js b/wwwroot/modules/main.js index 50a83ec..3b87ee8 100644 --- a/wwwroot/modules/main.js +++ b/wwwroot/modules/main.js @@ -3340,11 +3340,22 @@ function referenceImageFromTileMap(tileMapId) { const tileMap = getTileMapList().getTileMapById(tileMapId); if (!tileMap) throw new Error('Tile map not found.'); - console.log('Yes the tile map was found.'); + const palettes = tileMap.getPalettes().map((id) => getPaletteList().getPaletteById(id)); + const transIndex = tileMap.isSprite ? [0] : []; + const image = PaintUtil.createTileGridImage(tileMap, getTileSet(), palettes, transIndex); - const canv = new OffscreenCanvas(tileMap.columnCount * 8, tileMap.rowCount * 8); - const tileMapImage = PaintUtil.drawTileImage(canv.getContext('2d'), 0, 0, canv.width, canv.height, ) - const tileMapImage = PaintUtil.dr(canv.getContext('2d'), 0, 0, canv.width, canv.height, ) + instanceState.referenceImage = new ReferenceImage(); + instanceState.referenceImage.setImage(image); + instanceState.referenceImage.setBounds(0, 0, image.width, image.height); + + tileContextToolbar.setState({ + referenceBounds: instanceState.referenceImage.getBounds(), + referenceTransparency: instanceState.transparencyIndex + }); + tileEditor.setState({ + referenceImage: instanceState.referenceImage, + transparencyIndicies: getTransparencyIndicies() + }); } function clearReferenceImage() { diff --git a/wwwroot/modules/util/paintUtil.js b/wwwroot/modules/util/paintUtil.js index 0f48c43..97a04fd 100644 --- a/wwwroot/modules/util/paintUtil.js +++ b/wwwroot/modules/util/paintUtil.js @@ -232,15 +232,15 @@ export default class PaintUtil { * Draws a tile map onto a canvas object. * @param {TileGridProvider} tileGrid - Tile grid to draw. * @param {TileSet} tileSet - Tile set that contains the tiles used by the tile grid. - * @param {PaletteList} paletteList - Palettes to use when drawing. + * @param {Palette[]|PaletteList} palettes - Palettes to use when drawing. * @param {number[]?} [paletteIndiciesToRenderTransparent] - Palette indicies to render as transparent. * @returns {ImageBitmap} */ - static createTileGridImage(tileGrid, tileSet, paletteList, paletteIndiciesToRenderTransparent) { + static createTileGridImage(tileGrid, tileSet, palettes, paletteIndiciesToRenderTransparent) { const widthPx = tileGrid.columnCount * 8; const heightPx = tileGrid.rowCount * 8; const canvas = new OffscreenCanvas(widthPx, heightPx); - drawTileGridOntoCanvas(canvas, tileGrid, tileSet, paletteList, paletteIndiciesToRenderTransparent); + drawTileGridOntoCanvas(canvas, tileGrid, tileSet, palettes, paletteIndiciesToRenderTransparent); return canvas.transferToImageBitmap(); } @@ -399,13 +399,14 @@ function translateCoordinate(tileInfo, x, y) { * @param {HTMLCanvasElement|OffscreenCanvas} canvas - Canvas to draw onto. * @param {TileGridProvider} tileGrid - Tile grid to draw. * @param {TileSet} tileSet - Tile set that contains the tiles used by the tile grid. - * @param {PaletteList} paletteList - Palettes to use when drawing. + * @param {Palette[]|PaletteList} palettes - Palettes to use when drawing. */ -function drawTileGridOntoCanvas(canvas, tileGrid, tileSet, paletteList) { +function drawTileGridOntoCanvas(canvas, tileGrid, tileSet, palettes) { if (!canvas instanceof HTMLCanvasElement && !canvas instanceof OffscreenCanvas) throw new Error('Non valid canvas object was passed.'); if (!tileGrid instanceof TileGridProvider) throw new Error('Tile grid was not valid.'); if (!tileSet instanceof TileSet) throw new Error('Tile set was not valid.'); - if (!paletteList instanceof PaletteList) throw new Error('Palette list was not valid.'); + + const paletteArray = palettes instanceof PaletteList ? palettes.getPalettes() : palettes.map((p) => p instanceof Palette); const context = canvas.getContext('2d'); const dimensions = { w: 8, h: 8 }; @@ -414,7 +415,7 @@ function drawTileGridOntoCanvas(canvas, tileGrid, tileSet, paletteList) { for (let row = 0; row < tileGrid.rowCount; row++) { const tileInfo = tileGrid.getTileInfoByRowAndColumn(row, col); const tile = tileSet?.getTile(tileInfo.tileId) ?? null; - const palette = paletteList?.getPaletteByIndex(tileInfo.paletteIndex) ?? null; + const palette = paletteArray[tileInfo.paletteIndex]; const coords = { x: col * 8, y: row * 8 }; if (tile && palette) { drawTileImage(context, From e8bf26639a9fd8f38f824fd0bcceb39de757c16a Mon Sep 17 00:00:00 2001 From: Nicholas Furgiuele Date: Wed, 8 May 2024 21:15:55 +1000 Subject: [PATCH 7/8] Gret the "Use as reference" option working under tile map settings. --- wwwroot/modules/components/canvasManager.js | 2 +- wwwroot/modules/main.js | 4 ++-- wwwroot/modules/util/paintUtil.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wwwroot/modules/components/canvasManager.js b/wwwroot/modules/components/canvasManager.js index a87710c..9ff7c5f 100644 --- a/wwwroot/modules/components/canvasManager.js +++ b/wwwroot/modules/components/canvasManager.js @@ -1235,7 +1235,7 @@ export default class CanvasManager { const originalSmoothingEnabled = context.imageSmoothingEnabled; const originalSmoothingQuality = context.imageSmoothingQuality; - context.imageSmoothingEnabled = true; + context.imageSmoothingEnabled = false; context.imageSmoothingQuality = 'high'; const pxSize = coords.pxSize; diff --git a/wwwroot/modules/main.js b/wwwroot/modules/main.js index 3b87ee8..98c0ee3 100644 --- a/wwwroot/modules/main.js +++ b/wwwroot/modules/main.js @@ -3341,8 +3341,8 @@ function referenceImageFromTileMap(tileMapId) { if (!tileMap) throw new Error('Tile map not found.'); const palettes = tileMap.getPalettes().map((id) => getPaletteList().getPaletteById(id)); - const transIndex = tileMap.isSprite ? [0] : []; - const image = PaintUtil.createTileGridImage(tileMap, getTileSet(), palettes, transIndex); + const transparentIndicies = tileMap.isSprite ? [0] : []; + const image = PaintUtil.createTileGridImage(tileMap, getTileSet(), palettes, transparentIndicies); instanceState.referenceImage = new ReferenceImage(); instanceState.referenceImage.setImage(image); diff --git a/wwwroot/modules/util/paintUtil.js b/wwwroot/modules/util/paintUtil.js index 97a04fd..0e5c832 100644 --- a/wwwroot/modules/util/paintUtil.js +++ b/wwwroot/modules/util/paintUtil.js @@ -406,7 +406,7 @@ function drawTileGridOntoCanvas(canvas, tileGrid, tileSet, palettes) { if (!tileGrid instanceof TileGridProvider) throw new Error('Tile grid was not valid.'); if (!tileSet instanceof TileSet) throw new Error('Tile set was not valid.'); - const paletteArray = palettes instanceof PaletteList ? palettes.getPalettes() : palettes.map((p) => p instanceof Palette); + const paletteArray = palettes instanceof PaletteList ? palettes.getPalettes() : palettes.filter((p) => p instanceof Palette); const context = canvas.getContext('2d'); const dimensions = { w: 8, h: 8 }; @@ -414,7 +414,7 @@ function drawTileGridOntoCanvas(canvas, tileGrid, tileSet, palettes) { for (let col = 0; col < tileGrid.columnCount; col++) { for (let row = 0; row < tileGrid.rowCount; row++) { const tileInfo = tileGrid.getTileInfoByRowAndColumn(row, col); - const tile = tileSet?.getTile(tileInfo.tileId) ?? null; + const tile = tileSet?.getTileById(tileInfo.tileId) ?? null; const palette = paletteArray[tileInfo.paletteIndex]; const coords = { x: col * 8, y: row * 8 }; if (tile && palette) { From 4042d37cd4df5d21426d129cad8178109d13b37f Mon Sep 17 00:00:00 2001 From: Nicholas Furgiuele Date: Wed, 8 May 2024 21:35:12 +1000 Subject: [PATCH 8/8] Fix image export function. --- wwwroot/modules/main.js | 12 +++++++++++- wwwroot/modules/ui/tileEditor.js | 6 +++--- wwwroot/modules/worker/tileEditorViewportWorker.js | 6 +++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/wwwroot/modules/main.js b/wwwroot/modules/main.js index 98c0ee3..c54cb71 100644 --- a/wwwroot/modules/main.js +++ b/wwwroot/modules/main.js @@ -1056,7 +1056,7 @@ function handleExportToolbarOnCommand(args) { break; case ExportToolbar.Commands.exportImage: - tileEditor.setState({ requestExportImage: true }); + exportCurrentTileGridAsImage(); break; } @@ -4261,6 +4261,16 @@ function downloadAssemblyCode(code) { a.remove(); } +/** + * Exports the currently selected tile set or tile grid to an image. + */ +function exportCurrentTileGridAsImage() { + const tileGrid = getTileGrid(); + const palettes = getPaletteListToSuitTileMapOrTileSetSelection(); + const image = PaintUtil.createTileGridImage(tileGrid, getTileSet(), palettes); + exportImage(image); +} + /** * Exports tileset to an image. * @argument {ImageBitmap} image diff --git a/wwwroot/modules/ui/tileEditor.js b/wwwroot/modules/ui/tileEditor.js index b3e0bd3..54836f4 100644 --- a/wwwroot/modules/ui/tileEditor.js +++ b/wwwroot/modules/ui/tileEditor.js @@ -791,8 +791,8 @@ export default class TileEditor extends ComponentBase { canvasState.canvasHeight = response.canvasSize.height; canvasState.tileGridRows = response.tileGridDimension.rows; canvasState.tileGridColumns = response.tileGridDimension.columns; - canvasState.tileGridWidthPixels = response.tileGridImage.width; - canvasState.tileGridHeightPixels = response.tileGridImage.height; + canvasState.tileGridWidthPixels = response.tileGridImageDimensions.width; + canvasState.tileGridHeightPixels = response.tileGridImageDimensions.height; canvasState.offsetX = response.tileGridOffset.x; canvasState.offsetY = response.tileGridOffset.y; canvasState.scale = response.scale; @@ -867,7 +867,7 @@ export default class TileEditor extends ComponentBase { /** * @typedef {Object} TileEditorEventArgs * @property {string} event - Event that occurred. - * @property {number} tileGridImage - Image of the tile grid. + * @property {ImageBitmap} tileGridImage - Image of the tile grid. * @property {number} x - X tile grid pixel thats selected. * @property {number} y - Y tile grid pixel thats selected. * @property {number?} [tileGridRowIndex] - Index of the tile grid row that corresponds with the Y mouse coordinate. diff --git a/wwwroot/modules/worker/tileEditorViewportWorker.js b/wwwroot/modules/worker/tileEditorViewportWorker.js index ae20a60..6cdd244 100644 --- a/wwwroot/modules/worker/tileEditorViewportWorker.js +++ b/wwwroot/modules/worker/tileEditorViewportWorker.js @@ -274,12 +274,12 @@ function makeImageResponse() { hasTileSet: canvasManager.tileSet !== null, hasPalettes: canvasManager.paletteList !== null, isTileMap: canvasManager.tileGrid !== null && canvasManager.tileGrid instanceof TileMap, - tileGridBitmap: null, + tileGridImage: null, canvasSize: { width: canvas.width, height: canvas.height }, - tileGridImage: { + tileGridImageDimensions: { width: canvasManager.tileGrid ? (canvasManager.tileGrid.columnCount * 8 * canvasManager.scale) : 1, height: canvasManager.tileGrid ? (canvasManager.tileGrid.rowCount * 8 * canvasManager.scale) : 1 }, @@ -346,7 +346,7 @@ function makeImageResponse() { * @property {boolean} isTileMap * @property {ImageBitmap?} [tileGridImage] - An image of the tile grid. * @property {import('../types.js').Dimension} [canvasSize] - Pixel dimensions of the viewport. - * @property {import('../types.js').Dimension} [tileGridImage] - Pixel dimensions of the tile grid image. + * @property {import('../types.js').Dimension} [tileGridImageDimensions] - Pixel dimensions of the tile grid image. * @property {import('../types.js').GridDimension} [tileGridDimension] - Dimension of the tile grid in rows and columns. * @property {import('../types.js').Coordinate} [tileGridOffset] - Offset of the tile grid image relative to the centre of the viewport. * @property {number} [scale] - Image drawing scale, 1 = 1:1, 2 = 2:1, 15 = 15:1.