arcade-img-import/main.js
2021-07-22 08:53:24 +02:00

244 lines
No EOL
7 KiB
JavaScript

let mode = "full-width"
// https://makecode.com/_EvPP98M4pYEC
/**
* To do:
*
* 1. Implement "fit", "fill", "custom"
* 2. Black and white mode
* 3. Other cool effects
* 4. Shuffle colors
*/
const canvas = document.querySelector("canvas")
const copyButton = document.querySelector("button#copy")
const customSizes = document.querySelectorAll("input[type='number'].custom")
const fileInput = document.querySelector("input#myFile")
const form = document.querySelector("form")
const numberInputs = document.querySelectorAll("input[type='number']")
const radioButtons = document.querySelectorAll("input[type='radio']")
const scaleFactor = document.querySelector("input[type='number']#factor")
const textarea = document.querySelector("textarea")
let originalImageSize = {
width: 0,
height: 0
}
fileInput.addEventListener("change", function whenImageIsUploaded() {
const img = document.createElement("img")
img.src = window.URL.createObjectURL(this.files[0])
const node = document.querySelector("img")
if (node !== null) {
node.parentNode.removeChild(node)
}
document.body.appendChild(img)
img.addEventListener("load", () => {
originalImageSize.width = img.width
originalImageSize.height = img.height
mode = "full-width"
convert(img)
})
})
radioButtons.forEach(radioButton => {
radioButton.addEventListener("change", function sizeOption() {
mode = this.id
const numberInput = this.parentElement.querySelector("input[type='number']")
customSizes.forEach(field => field.disabled = (mode !== "custom"))
scaleFactor.disabled = (mode !== "scale")
scaleFactor.value = 0.1
img = document.querySelector("img")
convert(img)
})
})
form.addEventListener("submit", function convertImage(event) {
event.preventDefault()
const imageDOM = document.querySelector("img")
if (originalImageSize.width === 0 && originalImageSize.height === 0) {
originalImageSize.width = imageDOM.width
originalImageSize.height = imageDOM.height
}
img = document.querySelector("img")
convert(img)
resetImageSize(img)
})
function convert(img) {
// originalImageSize
copyButton.innerText = "Copy code" // Reset text if another image is uploaded
const arcadeColors = [
"#00000000", // Transparent
"#ffffff",
"#ff2121",
"#ff93c4",
"#ff8135",
"#fff609",
"#249ca3",
"#78dc52",
"#003fad",
"#87f2ff",
"#8e2ec4",
"#a4839f",
"#5c406c",
"#e5cdc4",
"#91463d",
"#000000",
].map(function convertFromHexToRGB(color, index) {
const r = parseInt(color[1] + color[2], 16) // parseInt("a", 16) === 10
const g = parseInt(color[3] + color[4], 16)
const b = parseInt(color[5] + color[6], 16)
return {
color: { r, g, b },
index: (index).toString(16) // (10).toString(16) === "a"
}
})
/**
* MakeCode Arcade is 160x120
*
* Full width:
* factor = 160 / img.width
* Full height:
* factor = 120 / img.height
* Custom width:
* factor = n / img.width
* Custom height:
* factor = n / img.height
*
* w *= factor
* h *= factor
*/
function setSpriteDimensions(type) {
let imageWidth = originalImageSize.width
let imageHeight = originalImageSize.height
let factor = 1
if (type === "custom") {
let customWidth = document.querySelector(".custom#width").value
let customHeight = document.querySelector(".custom#height").value
if (customWidth && !customHeight) {
const factor = customWidth / originalImageSize.width
imageWidth = customWidth
imageHeight *= factor
} else if (!customWidth && customHeight) {
const factor = customHeight / originalImageSize.height
imageWidth *= factor
imageHeight = customHeight
} else {
imageWidth = customWidth
imageHeight = customHeight
}
} else if (type === "scale") {
const factor = document.querySelector("input#factor").value
imageWidth *= factor
imageHeight *= factor
} else if (type === "full-width") {
const factor = 160 / imageWidth
imageWidth *= factor
imageHeight *= factor
} else if (type === "full-height") {
const factor = 120 / imageHeight
imageWidth *= factor
imageHeight *= factor
}
img.width = imageWidth
img.height = imageHeight
copyButton.innerText += ` (${img.width} x ${img.height})`
}
setSpriteDimensions(mode) // Mode is set when radio buttons are clicked. Default is full-width.
// Get the image's pixels and draw them onto a canvas element
// This way, we can loop through the pixels
canvas.width = img.width
canvas.height = img.height
const c = canvas.getContext("2d")
c.drawImage(img, 0, 0, canvas.width, canvas.height)
let pixelIndex = 0
let makeCodeString = {}
const data = c.getImageData(0, 0, canvas.width, canvas.height).data
// Canvas pixel values are stored as rgba: [r, g, b, a, r, g, b, a, ...]
for (let i = 0; i < data.length; i += 4) {
// This is how you get x and y coordinates from one variable
const x = pixelIndex % canvas.width
const y = Math.floor(pixelIndex / canvas.width)
const r = data[i + 0]
const g = data[i + 1]
const b = data[i + 2]
const a = data[i + 3]
/*
Now we have the rgba values for one pixel from the original image.
MakeCode colors are represented as index values from 0-15 (or really, 0-f).
We loop through the 16 color palette and pick the one that has
the closest r, g, and b values to the pixel we're checking.
*/
const nearest = arcadeColors.sort((prev, curr) => {
const rDifference = Math.abs(prev.color.r - r) - Math.abs(curr.color.r - r)
const gDifference = Math.abs(prev.color.g - g) - Math.abs(curr.color.g - g)
const bDifference = Math.abs(prev.color.b - b) - Math.abs(curr.color.b - b)
return rDifference + gDifference + bDifference
})[0]
// Draw a preview
c.fillStyle = `rgb(${nearest.color.r}, ${nearest.color.g}, ${nearest.color.b})`
c.fillRect(x, y, 1, 1)
/*
makeCodeString is a piece of working code that can be directly
pasted into MakeCode's JavaScript window.
*/
if (makeCodeString[`row-${y}`] === undefined) {
makeCodeString[`row-${y}`] = ""
} else {
if (nearest.index == 0) {
// 0 is transparent, f is black.
makeCodeString[`row-${y}`] += "f"
} else {
makeCodeString[`row-${y}`] += nearest.index
}
}
pixelIndex++
}
// Loop through the makeCodeString object to create the output
let dateString = new Date()
.toISOString()
.replaceAll("-", "")
.replaceAll(":", "")
.replaceAll(".", "")
let spriteJavaScript = `let mySprite${dateString} = sprites.create(img\``
for (const row in makeCodeString) {
spriteJavaScript += makeCodeString[row] + "\n"
}
spriteJavaScript += "`, SpriteKind.Player)"
// Copy text when user clicks button
// Sure, they can copy it themselves, but it's good to do nice things sometimes.
textarea.textContent = spriteJavaScript
copyButton.removeAttribute("disabled")
copyButton.addEventListener("click", function addCodeToClipboard() {
textarea.select()
document.execCommand("copy")
console.log("copy!");
copyButton.innerText = "Code copied to clipboard!"
resetImageSize(img)
})
}
function resetImageSize(img) {
img.width = originalImageSize.width
img.height = originalImageSize.height
}