mirror of
https://github.com/SwingTheVine/Wplace-BlueMarble.git
synced 2026-05-06 15:49:26 +00:00
458 lines
38 KiB
HTML
458 lines
38 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>Template.js - Documentation</title>
|
|
|
|
<script src="scripts/prettify/prettify.js"></script>
|
|
<script src="scripts/prettify/lang-css.js"></script>
|
|
<!--[if lt IE 9]>
|
|
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
<![endif]-->
|
|
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
|
|
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
|
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
|
</head>
|
|
<body>
|
|
|
|
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
|
|
<label for="nav-trigger" class="navicon-button x">
|
|
<div class="navicon"></div>
|
|
</label>
|
|
|
|
<label for="nav-trigger" class="overlay"></label>
|
|
|
|
<nav>
|
|
<li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading">Classes</li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="ApiManager.html">ApiManager</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="ConfettiManager.html">ConfettiManager</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Observers.html">Observers</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Overlay.html">Overlay</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="SettingsManager.html">SettingsManager</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Template.html">Template</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="TemplateManager.html">TemplateManager</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowCredits.html">WindowCredits</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowFilter.html">WindowFilter</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowMain.html">WindowMain</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowSettings.html">WindowSettings</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowTelemetry.html">WindowTelemetry</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowWizard.html">WindowWizard</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="module.exports_module.exports.html">exports</a></span></li><li class="nav-heading"><a href="global.html">Globals</a></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addBr">addBr</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addButton">addButton</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addButtonHelp">addButtonHelp</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addCaption">addCaption</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addCheckbox">addCheckbox</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addDetails">addDetails</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addDiv">addDiv</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addDragbar">addDragbar</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addFieldset">addFieldset</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addForm">addForm</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addHeader">addHeader</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addHr">addHr</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addImg">addImg</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addInput">addInput</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addInputFile">addInputFile</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addLegend">addLegend</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addLi">addLi</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addMenu">addMenu</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addOl">addOl</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addOption">addOption</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addP">addP</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addSelect">addSelect</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addSmall">addSmall</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addSpan">addSpan</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addSummary">addSummary</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTable">addTable</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTbody">addTbody</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTd">addTd</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTextarea">addTextarea</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTfoot">addTfoot</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTh">addTh</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addThead">addThead</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTimer">addTimer</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTr">addTr</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addUl">addUl</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#base64ToUint8">base64ToUint8</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildElement">buildElement</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildHighlight">buildHighlight</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildOverlay">buildOverlay</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildTemplate">buildTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildWindow">buildWindow</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildWindowed">buildWindowed</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#calculateCanvasTransparency">calculateCanvasTransparency</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#calculateCoordsFromChunked">calculateCoordsFromChunked</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#calculateRelativeLuminance">calculateRelativeLuminance</a></span></li><li class="nav-item"><span class="nav-item-type type-member">M</span><span class="nav-item-name"><a href="global.html#colorpalette">colorpalette</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#colorpaletteForBlueMarble">colorpaletteForBlueMarble</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#consoleError">consoleError</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#consoleLog">consoleLog</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#consoleWarn">consoleWarn</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#convertTemplateToBlob">convertTemplateToBlob</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createConfetti">createConfetti</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createJSON">createJSON</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createObserverBody">createObserverBody</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createTemplate">createTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createTemplateTiles">createTemplateTiles</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#deleteTemplate">deleteTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#disableTemplate">disableTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#downloadAllTemplates">downloadAllTemplates</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#downloadAllTemplatesFromStorage">downloadAllTemplatesFromStorage</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#downloadTemplate">downloadTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#drawTemplateOnTile">drawTemplateOnTile</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#encodedToNumber">encodedToNumber</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#escapeHTML">escapeHTML</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getClipboardData">getClipboardData</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getObserverBody">getObserverBody</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getWplaceVersion">getWplaceVersion</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#handleDisplayError">handleDisplayError</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#handleDisplayStatus">handleDisplayStatus</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#handleDrag">handleDrag</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#handleMinimization">handleMinimization</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#hexToRGB">hexToRGB</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#importJSON">importJSON</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#inject">inject</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#localizeDate">localizeDate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#localizeDuration">localizeDuration</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#localizeNumber">localizeNumber</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#localizePercent">localizePercent</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#negativeSafeModulo">negativeSafeModulo</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#numberToEncoded">numberToEncoded</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#observe">observe</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#observeBlack">observeBlack</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#rgbToHex">rgbToHex</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#selectAllCoordinateInputs">selectAllCoordinateInputs</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#serverTPtoDisplayTP">serverTPtoDisplayTP</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#setApiManager">setApiManager</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#setSettingsManager">setSettingsManager</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#setTemplatesShouldBeDrawn">setTemplatesShouldBeDrawn</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#setWindowMain">setWindowMain</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#sleep">sleep</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#spontaneousResponseListener">spontaneousResponseListener</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#toggleFlag">toggleFlag</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#uint8ToBase64">uint8ToBase64</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#updateColorList">updateColorList</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#updateInnerHTML">updateInnerHTML</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#updateUserStorage">updateUserStorage</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#viewCanvasInNewTab">viewCanvasInNewTab</a></span></li>
|
|
</nav>
|
|
|
|
<div id="main">
|
|
|
|
<h1 class="page-title">Template.js</h1>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
<article>
|
|
<pre class="prettyprint source linenums"><code>import { sleep, uint8ToBase64, viewCanvasInNewTab } from "./utils";
|
|
|
|
/** An instance of a template.
|
|
* Handles all mathematics, manipulation, and analysis regarding a single template.
|
|
* @class Template
|
|
* @since 0.65.2
|
|
*/
|
|
export default class Template {
|
|
|
|
/** The constructor for the {@link Template} class with enhanced pixel tracking.
|
|
* @param {Object} [params={}] - Object containing all optional parameters
|
|
* @param {string} [params.displayName='My template'] - The display name of the template
|
|
* @param {number} [params.sortID=0] - The sort number of the template for rendering priority
|
|
* @param {string} [params.authorID=''] - The user ID of the person who exported the template (prevents sort ID collisions)
|
|
* @param {string} [params.url=''] - The URL to the source image
|
|
* @param {File} [params.file=null] - The template file (pre-processed File or processed bitmap)
|
|
* @param {Array<number, number, number, number>} [params.coords=null] - The coordinates of the top left corner as (tileX, tileY, pixelX, pixelY)
|
|
* @param {Object} [params.chunked=null] - The affected chunks of the template, and their template for each chunk as a bitmap
|
|
* @param {Object} [params.chunked32={}] - The affected chunks of the template, and their template for each chunk as a Uint32Array
|
|
* @param {number} [params.tileSize=1000] - The size of a tile in pixels (assumes square tiles)
|
|
* @param {Object} [params.pixelCount={total:0, colors:Map}] - Total number of pixels in the template (calculated automatically during processing)
|
|
* @since 0.65.2
|
|
*/
|
|
constructor({
|
|
displayName = 'My template',
|
|
sortID = 0,
|
|
authorID = '',
|
|
url = '',
|
|
file = null,
|
|
coords = null,
|
|
chunked = null,
|
|
chunked32 = {},
|
|
tileSize = 1000,
|
|
} = {}) {
|
|
this.displayName = displayName;
|
|
this.sortID = sortID;
|
|
this.authorID = authorID;
|
|
this.url = url;
|
|
this.file = file;
|
|
this.coords = coords;
|
|
this.chunked = chunked;
|
|
this.chunked32 = chunked32;
|
|
this.tileSize = tileSize;
|
|
/** Total pixel count in template @type {{total: number, colors: Map<number, number>, correct?: { [key: string]: Map<number, number> }}} */
|
|
this.pixelCount = { total: 0, colors: new Map() };
|
|
|
|
this.shouldSkipTransTiles = true; // Should transparent template tiles be skipped during template creation?
|
|
this.shouldAggSkipTransTiles = false; // Should transparent template tiles be aggressively skipped during tempalte creation?
|
|
}
|
|
|
|
/** Creates chunks of the template for each tile.
|
|
* @param {Number} tileSize - Size of the tile as determined by templateManager
|
|
* @param {Object} paletteBM - An collection of Uint32Arrays containing the palette BM uses
|
|
* @param {boolean} shouldSkipTransTiles - Should transparent tiles be skipped over when creating the template?
|
|
* @param {boolean} shouldAggSkipTransTiles - Should transparent tiles be aggressively skipped over when creating the template?
|
|
* @returns {Object} Collection of template bitmaps & buffers organized by tile coordinates
|
|
* @since 0.65.4
|
|
*/
|
|
async createTemplateTiles(tileSize, paletteBM, shouldSkipTransTiles, shouldAggSkipTransTiles) {
|
|
console.log('Template coordinates:', this.coords);
|
|
|
|
// Updates the class instance variable with the new information
|
|
this.shouldSkipTransTiles = shouldSkipTransTiles;
|
|
this.shouldAggSkipTransTiles = shouldAggSkipTransTiles;
|
|
|
|
const shreadSize = 3; // Scale image factor for pixel art enhancement (must be odd)
|
|
const bitmap = await createImageBitmap(this.file); // Create efficient bitmap from uploaded file
|
|
const imageWidth = bitmap.width;
|
|
const imageHeight = bitmap.height;
|
|
|
|
this.tileSize = tileSize; // Tile size predetermined by the templateManager
|
|
|
|
const templateTiles = {}; // Holds the template tiles
|
|
const templateTilesBuffers = {}; // Holds the buffers of the template tiles
|
|
|
|
// The main canvas used during template creation
|
|
const canvas = new OffscreenCanvas(this.tileSize, this.tileSize);
|
|
const context = canvas.getContext('2d', { willReadFrequently: true });
|
|
|
|
// The canvas used to check if a specific template tile is transparent or not
|
|
const transCanvas = new OffscreenCanvas(this.tileSize, this.tileSize);
|
|
const transContext = transCanvas.getContext('2d', { willReadFrequently: true });
|
|
|
|
// Makes it so that `.drawImage()` calls on the canvas used to calculate transparency always draw below what is already on the canvas
|
|
transContext.globalCompositeOperation = "destination-over";
|
|
|
|
// Prep the canvas for drawing the entire template (so we can find total pixels)
|
|
canvas.width = imageWidth;
|
|
canvas.height = imageHeight;
|
|
context.imageSmoothingEnabled = false; // Nearest neighbor
|
|
|
|
context.drawImage(bitmap, 0, 0); // Draws the template to the canvas
|
|
|
|
let timer = Date.now();
|
|
const totalPixelMap = this.#calculateTotalPixelsFromImageData(context.getImageData(0, 0, imageWidth, imageHeight), paletteBM); // Calculates total pixels from the template buffer retrieved from the canvas context image data
|
|
console.log(`Calculating total pixels took ${(Date.now() - timer) / 1000.0} seconds`);
|
|
|
|
let totalPixels = 0; // Will store the total amount of non-Transparent color pixels
|
|
const transparentColorID = 0; // Color ID for the Transparent color
|
|
|
|
// For each color in the total pixel Map...
|
|
for (const [color, total] of totalPixelMap) {
|
|
|
|
if (color == transparentColorID) {continue;} // Skip Transparent color
|
|
|
|
totalPixels += total; // Adds the total amount for the pixel color to the total amount for all colors
|
|
}
|
|
|
|
this.pixelCount = { total: totalPixels, colors: totalPixelMap }; // Stores the total pixel count in the Template instance
|
|
|
|
timer = Date.now();
|
|
|
|
// Creates a mask where the middle pixel is white, and everything else is transparent
|
|
const canvasMask = new OffscreenCanvas(3, 3);
|
|
const contextMask = canvasMask.getContext("2d");
|
|
contextMask.clearRect(0, 0, 3, 3);
|
|
contextMask.fillStyle = "white";
|
|
contextMask.fillRect(1, 1, 1, 1);
|
|
|
|
// For every tile...
|
|
for (let pixelY = this.coords[3]; pixelY < imageHeight + this.coords[3];) {
|
|
|
|
// Draws the partial tile first, if any
|
|
// This calculates the size based on which is smaller:
|
|
// A. The top left corner of the current tile to the bottom right corner of the current tile
|
|
// B. The top left corner of the current tile to the bottom right corner of the image
|
|
const drawSizeY = Math.min(this.tileSize - (pixelY % this.tileSize), imageHeight - (pixelY - this.coords[3]));
|
|
|
|
console.log(`Math.min(${this.tileSize} - (${pixelY} % ${this.tileSize}), ${imageHeight} - (${pixelY - this.coords[3]}))`);
|
|
|
|
for (let pixelX = this.coords[2]; pixelX < imageWidth + this.coords[2];) {
|
|
|
|
console.log(`Pixel X: ${pixelX}\nPixel Y: ${pixelY}`);
|
|
|
|
// Draws the partial tile first, if any
|
|
// This calculates the size based on which is smaller:
|
|
// A. The top left corner of the current tile to the bottom right corner of the current tile
|
|
// B. The top left corner of the current tile to the bottom right corner of the image
|
|
const drawSizeX = Math.min(this.tileSize - (pixelX % this.tileSize), imageWidth - (pixelX - this.coords[2]));
|
|
|
|
// If the user wants to skip any tiles where the template is transparent...
|
|
if (shouldSkipTransTiles) {
|
|
|
|
// Detects if the canvas is fully transparent
|
|
const isTemplateTileTransparent = !this.calculateCanvasTransparency({
|
|
bitmap: bitmap,
|
|
bitmapParams: [pixelX - this.coords[2], pixelY - this.coords[3], drawSizeX, drawSizeY], // Top left X, Top left Y, Width, Height
|
|
transCanvas: transCanvas,
|
|
transContext: transContext
|
|
});
|
|
|
|
console.log(`Tile contains template: ${!isTemplateTileTransparent}`);
|
|
|
|
// If the template in this tile is transparent...
|
|
if (isTemplateTileTransparent) {
|
|
pixelX += drawSizeX; // If you remove this, it will get stuck forever processing the template
|
|
continue; // ...the user does not want to save this tile, so we skip to the next tile
|
|
}
|
|
}
|
|
|
|
console.log(`Math.min(${this.tileSize} - (${pixelX} % ${this.tileSize}), ${imageWidth} - (${pixelX - this.coords[2]}))`);
|
|
|
|
console.log(`Draw Size X: ${drawSizeX}\nDraw Size Y: ${drawSizeY}`);
|
|
|
|
// Change the canvas size and wipe the canvas
|
|
const canvasWidth = drawSizeX * shreadSize;// + (pixelX % this.tileSize) * shreadSize;
|
|
const canvasHeight = drawSizeY * shreadSize;// + (pixelY % this.tileSize) * shreadSize;
|
|
canvas.width = canvasWidth;
|
|
canvas.height = canvasHeight;
|
|
|
|
console.log(`Draw X: ${drawSizeX}\nDraw Y: ${drawSizeY}\nCanvas Width: ${canvasWidth}\nCanvas Height: ${canvasHeight}`);
|
|
|
|
context.imageSmoothingEnabled = false; // Nearest neighbor
|
|
|
|
console.log(`Getting X ${pixelX}-${pixelX + drawSizeX}\nGetting Y ${pixelY}-${pixelY + drawSizeY}`);
|
|
|
|
// Draws the template segment on this tile segment
|
|
context.clearRect(0, 0, canvasWidth, canvasHeight); // Clear any previous drawing (only runs when canvas size does not change)
|
|
context.drawImage(
|
|
bitmap, // Bitmap image to draw
|
|
pixelX - this.coords[2], // Coordinate X to draw *from*
|
|
pixelY - this.coords[3], // Coordinate Y to draw *from*
|
|
drawSizeX, // X width to draw *from*
|
|
drawSizeY, // Y height to draw *from*
|
|
0, // Coordinate X to draw *at*
|
|
0, // Coordinate Y to draw *at*
|
|
drawSizeX * shreadSize, // X width to draw *at*
|
|
drawSizeY * shreadSize // Y height to draw *at*
|
|
); // Coordinates and size of draw area of source image, then canvas
|
|
|
|
context.save(); // Saves the current context of the canvas
|
|
context.globalCompositeOperation = "destination-in"; // The existing canvas content is kept where both the new shape and existing canvas content overlap. Everything else is made transparent.
|
|
// For our purposes, this means any non-transparent pixels on the mask will be kept
|
|
|
|
console.log(`Should Skip: ${shouldSkipTransTiles}; Should Agg Skip: ${shouldAggSkipTransTiles}`);
|
|
|
|
// Fills the canvas with the mask
|
|
context.fillStyle = context.createPattern(canvasMask, "repeat");
|
|
context.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
|
|
context.restore(); // Restores the context of the canvas to the previous save
|
|
|
|
const imageData = context.getImageData(0, 0, canvasWidth, canvasHeight); // Data of the image on the canvas
|
|
|
|
console.log(`Shreaded pixels for ${pixelX}, ${pixelY}`, imageData);
|
|
|
|
// Creates the "0000,0000,000,000" key name
|
|
const templateTileName = `${
|
|
(this.coords[0] + Math.floor(pixelX / 1000)).toString().padStart(4, '0')},${
|
|
(this.coords[1] + Math.floor(pixelY / 1000)).toString().padStart(4, '0')},${
|
|
(pixelX % 1000).toString().padStart(3, '0')},${
|
|
(pixelY % 1000).toString().padStart(3, '0')
|
|
}`;
|
|
|
|
this.chunked32[templateTileName] = new Uint32Array(imageData.data.buffer); // Creates the Uint32Array
|
|
|
|
templateTiles[templateTileName] = await createImageBitmap(canvas); // Creates the bitmap
|
|
|
|
const canvasBlob = await canvas.convertToBlob();
|
|
const canvasBuffer = await canvasBlob.arrayBuffer();
|
|
const canvasBufferBytes = Array.from(new Uint8Array(canvasBuffer));
|
|
templateTilesBuffers[templateTileName] = uint8ToBase64(canvasBufferBytes); // Stores the buffer
|
|
|
|
console.log(templateTiles);
|
|
|
|
pixelX += drawSizeX;
|
|
}
|
|
|
|
pixelY += drawSizeY;
|
|
}
|
|
|
|
console.log(`Parsing template took ${(Date.now() - timer) / 1000.0} seconds`);
|
|
console.log('Template Tiles: ', templateTiles);
|
|
console.log('Template Tiles Buffers: ', templateTilesBuffers);
|
|
console.log('Template Tiles Uint32Array: ', this.chunked32);
|
|
return { templateTiles, templateTilesBuffers };
|
|
}
|
|
|
|
/** Detects if the canvas is transparent.
|
|
* @param {Object} param - Object that contains the parameters for the function
|
|
* @param {ImageBitmap} param.bitmap - The bitmap template image
|
|
* @param {Array<number, number, number, number>} param.bitmapParams - The parameters to obtain the template tile image from the bitmap
|
|
* @param {OffscreenCanvas | HTMLCanvasElement} param.transCanvas - The canvas to draw to in order to calculate this
|
|
* @param {OffscreenCanvasRenderingContext2D} param.transContext - The context for the transparent canvas to draw to
|
|
* @return {boolean} Is the canvas transparent? If transparent, then `true` is returned. Otherwise, `false`.
|
|
* @since 0.91.75
|
|
*/
|
|
calculateCanvasTransparency({
|
|
bitmap: bitmap,
|
|
bitmapParams: bitmapParams,
|
|
transCanvas: transCanvas,
|
|
transContext: transContext
|
|
}) {
|
|
|
|
console.log(`Calculating template tile transparency...`);
|
|
|
|
console.log(`Should Skip: ${this.shouldSkipTransTiles}; Should Agg: ${this.shouldAggSkipTransTiles}`);
|
|
|
|
const timer = Date.now(); // Starts the timer
|
|
|
|
// Contains the directions to move the canvas when duplicating, in the unit of pixels
|
|
const duplicationCoordinateArray = [
|
|
[ 0, 1], // E.g. move 0 on the x axis, and 1 down on the y axis
|
|
[ 1, 0],
|
|
[ 0, -2], // E.g. move 0 on the x axis, and 2 up on the y axis
|
|
[ -2, 0],
|
|
[ 0, 4],
|
|
[ 4, 0],
|
|
[ 0, -8],
|
|
[ -8, 0],
|
|
[ 0, 16],
|
|
[ 16, 0],
|
|
[ 0, -32],
|
|
[-32, 0]
|
|
];
|
|
|
|
// Changes the size of the canvas so that it equals the template tile
|
|
const transCanvasWidth = bitmapParams[2];
|
|
const transCanvasHeight = bitmapParams[3];
|
|
transCanvas.width = transCanvasWidth;
|
|
transCanvas.height = transCanvasHeight;
|
|
|
|
transContext.clearRect(0, 0, transCanvasWidth, transCanvasHeight); // Clear any previous drawing (only runs when canvas size does not change)
|
|
|
|
// If the user does want to aggressively skip transparent template tiles...
|
|
if (this.shouldAggSkipTransTiles) {
|
|
// (This code will only run if `this.shouldSkipTransTiles` is `true`)
|
|
|
|
// Draw the template tile onto the canvas scaled down to 10x10
|
|
transContext.drawImage(
|
|
bitmap, // The bitmap image
|
|
...bitmapParams, // Bitmap image parameters (x, y, width, height)
|
|
0, 0, // The coordinate draw the output *at*
|
|
10, 10 // The width and height of the output
|
|
);
|
|
} else {
|
|
// Else, the user wants to skip transparent template tiles normally...
|
|
|
|
// Draw the template tile onto the canvas
|
|
transContext.drawImage(
|
|
bitmap, // The bitmap image
|
|
...bitmapParams, // Bitmap image parameters (x, y, width, height)
|
|
0, 0, // The coordinate draw the output *at*
|
|
transCanvasWidth, transCanvasHeight // Stretch to canvas (the canvas should already be the same size as the template image)
|
|
)
|
|
|
|
// For each canvas duplication...
|
|
for (const [relativeX, relativeY] of duplicationCoordinateArray) {
|
|
|
|
// Duplicate the canvas onto itself, but shifted slightly
|
|
transContext.drawImage(
|
|
transCanvas, // The canvas we are drawing to *is* the source image
|
|
0, 0, transCanvasWidth, transCanvasHeight, // The entire canvas (as a source image)
|
|
relativeX, relativeY, transCanvasWidth, transCanvasHeight // The output coordinates and size on the same canvas
|
|
)
|
|
}
|
|
|
|
// Scale down the image to 10x10, and store it between (0, 0) and (9, 9) on the canvas
|
|
transContext.drawImage(
|
|
transCanvas, // The canvas we are drawing to *is* the source image
|
|
0, 0, transCanvasWidth, transCanvasHeight, // The entire canvas (as a source image)
|
|
0, 0, 10, 10 // The output coordinates and size on the same canvas
|
|
);
|
|
}
|
|
|
|
const shunkCanvas = transContext.getImageData(0, 0, 10, 10);
|
|
const shunkCanvas32 = new Uint32Array(shunkCanvas.data.buffer);
|
|
|
|
console.log(`Calculated canvas transparency in ${(Date.now() - timer) / 1000} seconds.`);
|
|
|
|
// For every pixel in the `shrunkCanvas32` array...
|
|
for (const pixel of shunkCanvas32) {
|
|
|
|
// If the pixel is NOT 100% transparent
|
|
if (!!pixel) {
|
|
return true; // Return `true` early since we confirmed a template exists in the tile
|
|
}
|
|
}
|
|
|
|
return false; // Since we could not confirm any template exists, we assume no template eixsts in this tile
|
|
}
|
|
|
|
/** Calculates top left coordinate of template.
|
|
* It uses `Template.chunked` to update `Template.coords`
|
|
* @since 0.88.504
|
|
*/
|
|
calculateCoordsFromChunked() {
|
|
let topLeftCoord = [Infinity, Infinity, Infinity, Infinity];
|
|
const tileKeys = Object.keys(this.chunked).sort(); // Sorts the tile keys
|
|
tileKeys.forEach((key, index) => { // For each tile key...
|
|
const [tileX, tileY, pixelX, pixelY] = key.split(',').map(Number); // Deconstruct the tile key
|
|
if ((tileY < topLeftCoord[1]) || (tileY == topLeftCoord[1] && tileX < topLeftCoord[0])) {
|
|
topLeftCoord = [tileX, tileY, pixelX, pixelY]; // Record the smallest tile key coordinates. Otherwise, use previous best
|
|
}
|
|
});
|
|
this.coords = topLeftCoord;
|
|
}
|
|
|
|
/** Calculates the total pixels for each color for the image.
|
|
*
|
|
* @param {ImageData} imageData - The pre-shreaded image "casted" onto a canvas
|
|
* @param {Object} paletteBM - The palette Blue Marble uses for colors
|
|
* @param {Number} paletteTolerance - How close an RGB color has to be in order to be considered a palette color. A tolerance of "3" means the sum of the RGB can be up to 3 away from the actual value.
|
|
* @returns {Map<Number, Number>} A map where the key is the color ID, and the value is the total pixels for that color ID
|
|
* @since 0.88.6
|
|
*/
|
|
#calculateTotalPixelsFromImageData(imageData, paletteBM) {
|
|
|
|
const buffer32Arr = new Uint32Array(imageData.data.buffer); // RGB values as a Uint32Array. Each index represents 1 pixel.
|
|
const { palette: _, LUT: lookupTable } = paletteBM; // Obtains the palette and LUT
|
|
|
|
// Makes a copy of the color palette Blue Marble uses, turns it into a Map, and adds data to count the amount of each color
|
|
const _colorpalette = new Map(); // Temp color palette
|
|
|
|
// For every pixel...
|
|
for (let pixelIndex = 0; pixelIndex < buffer32Arr.length; pixelIndex++) {
|
|
|
|
const pixel = buffer32Arr[pixelIndex]; // Current pixel to check
|
|
let bestColorID = -2; // Will eventually store the best match for color ID
|
|
|
|
// If the pixel is transparent...
|
|
if ((pixel >>> 24) == 0) {
|
|
bestColorID = 0; // Set the color ID to 0
|
|
} else {
|
|
// Else, look up the color ID in the "cube" LUT. If none is found, fallback to -2 ("Other")
|
|
bestColorID = lookupTable.get(pixel) ?? -2;
|
|
}
|
|
|
|
// Increments the count by 1 for the best matching color ID (which can be negative).
|
|
// If the color ID has not been counted yet, default to 1
|
|
const colorIDcount = _colorpalette.get(bestColorID);
|
|
_colorpalette.set(bestColorID, colorIDcount ? colorIDcount + 1 : 1);
|
|
}
|
|
|
|
console.log(_colorpalette);
|
|
return _colorpalette;
|
|
}
|
|
}
|
|
</code></pre>
|
|
</article>
|
|
</section>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<br class="clear">
|
|
|
|
<footer>
|
|
Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme.
|
|
</footer>
|
|
|
|
<script>prettyPrint();</script>
|
|
<script src="scripts/linenumber.js"></script>
|
|
</body>
|
|
</html>
|