mirror of
https://github.com/codeninjasuk/arcade-img-import.git
synced 2025-05-15 07:31:15 -04:00
244 lines
No EOL
7 KiB
JavaScript
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
|
|
} |