46 Commits

Author SHA1 Message Date
7c444cb35d add support for Bitsy 7.10; turn dithering off by default; more help text 2021-11-06 11:15:34 +00:00
a2c92b1e12 move deprecated function call to replacement function 2021-07-03 11:31:06 +01:00
57b841ac3d add clipboard button 2021-07-03 11:29:59 +01:00
3bbf51e5f8 rename download to "done!" 2021-07-03 11:29:26 +01:00
7af54d938b things are ok 2021-07-03 11:28:52 +01:00
f224fe1e27 remove console log 2021-07-03 11:28:33 +01:00
e6fda7266e version update 2021-07-03 11:28:12 +01:00
f223e51195 version update 2021-07-03 11:27:58 +01:00
d72a4df55e remove unused image header 2021-07-03 11:27:27 +01:00
0f23b878b2 version update 2021-05-02 11:47:10 +01:00
23b99b8ea7 fix loading game from textarea (copy/paste) 2021-05-02 11:45:06 +01:00
c8b6c772ab remove debug 2021-04-25 18:49:00 +01:00
52728e601b remove unnecessary trailing semicolon 2021-04-25 17:47:39 +01:00
4bd6286cf0 replace to_string with into for brevity 2021-04-25 17:47:25 +01:00
d53244a884 this is no longer a special feature 2021-04-25 17:46:52 +01:00
f6678f28a9 fix this old broken test 2021-04-25 17:46:25 +01:00
b157007ff3 restrict file types and add help text about game data 2020-11-28 18:33:11 +00:00
704f710047 fix syntax 2020-11-28 15:57:19 +00:00
5c4259d222 better splash page for now 2020-11-28 15:15:40 +00:00
47a9bf5c79 explain 2020-11-09 17:48:26 +00:00
d100473542 try to check for webassembly in a different place 2020-11-09 11:55:11 +00:00
d0946d3ab5 readme; remove todo as it's now in the readme and on gitea 2020-11-09 11:54:45 +00:00
5ee1e908f1 link won't open in same iframe 2020-11-08 21:29:21 +00:00
5788baa0b8 new host for old version 2020-11-08 21:27:05 +00:00
f135e184e4 Merge branch 'Refactor3' 2020-11-08 21:19:10 +00:00
3e7d6eeaa5 include old version 2020-11-08 21:18:51 +00:00
f6308110be try again 2020-11-08 20:53:52 +00:00
2d73963aa0 black and white palette 2020-11-08 20:52:29 +00:00
6f8e00130c update deploy script 2020-11-08 20:50:55 +00:00
b478b1e3ee update deploy script 2020-11-08 20:50:42 +00:00
055928eb7b todo 2020-11-08 20:48:04 +00:00
b3690c4dd7 black and white palette 2020-11-08 20:46:58 +00:00
da04534fd9 fix crop shit 2020-11-08 20:45:10 +00:00
3d1129c613 log 2020-11-08 20:07:48 +00:00
3b851975d0 room stats 2020-11-08 20:07:42 +00:00
afc626bae0 undo skipping crop on 128² 2020-11-08 20:07:26 +00:00
6e43249d64 done 2020-11-08 20:06:08 +00:00
458604cd1a display number of tiles added 2020-11-08 17:36:24 +00:00
bb0b970281 bump 2020-11-08 17:36:11 +00:00
f62202cb74 style header differently 2020-11-08 17:36:00 +00:00
745b18dfc0 styling tweaks 2020-11-08 17:35:28 +00:00
e5a87f854e this would break things 2020-11-08 17:35:08 +00:00
c2787db422 better load_image errors 2020-11-08 17:34:48 +00:00
7d274bb3c2 this function doesn't return anything 2020-11-08 17:33:39 +00:00
fbe40fb866 dedupe palette function 2020-11-08 17:33:15 +00:00
21eb632d22 add old version 2020-11-07 19:30:20 +00:00
15 changed files with 453 additions and 227 deletions

View File

@@ -1,19 +1,19 @@
[package]
name = "pixsy"
version = "0.72.6"
version = "0.710.0"
description = "convert images to Bitsy rooms"
authors = ["Max Bradbury <max@tinybird.info>"]
edition = "2018"
license = "MIT"
repository = "https://tinybird.dev/max/image-to-bitsy"
repository = "https://tinybird.dev/max/pixsy"
[lib]
crate-type = ["cdylib"]
[dependencies]
base64 = "^0.12.3"
bitsy-parser = "^0.72.5"
bitsy-parser = "^0.710.0"
image = "^0.23.7"
json = "^0.12.4"
lazy_static = "^1.4.0"
wasm-bindgen = "=0.2.64" # newer versions are bugged...
wasm-bindgen = "^0.2.78"

View File

@@ -1,3 +1,57 @@
# pixsy
convert images to rooms for use in Bitsy
a tool for [Bitsy Game Maker](http://bitsy.org).
upload any image and convert it into a room.
## credits
made by [Max Bradbury](http://tinybird.info/).
makes use of my own [bitsy parser](https://crates.io/crates/bitsy-parser) library.
uses the [Croppie](https://foliotek.github.io/Croppie/) image crop plugin
by [Foliotek](https://www.foliotek.com/)
uses [wasm-bindgen](https://crates.io/crates/wasm-bindgen) to automate WebAssembly bindings.
## thanks
to [Adam Le Doux](http://ledoux.io/) for creating the wonderful and inspiring Bitsy
to [Mark Wonnacott](https://kool.tools/) for their support, encouragement and inspiration
and to everyone in the bitsy community!
## contributing
forks and pull requests welcome!
### development prerequisites
* [rust/cargo](https://rustup.rs/)
* [pug](https://pugjs.org/)
* [less](http://lesscss.org/)
* a bash shell for the build script
## bugs
when importing images, some pixels have errors.
it seems to only happen for pixels surrounded at the top and left:
```
111 111
100 => 110
100 100
```
pixsy does not work in the Itch desktop program
because their bundled version of Chromium does not support WebAssembly.
## to do
* add alternative dithering options (Atkinson, Bayer 8×8)
* add a 'smoothing' (noise reduction?) stage to remove errant pixels
## could do
* reimplement tile reuse option
* add camera support so users can take a pic instead of uploading an image
* allow user to draw to canvas

View File

@@ -1,8 +0,0 @@
# todo
* if image is exactly 128×128, *don't* crop
* tile reuse
* noise reduction (remove lonely pixels)
* implement Atkinson and Bayer dithering options
* stats for added room (number of tiles)
* dedupe "palette from custom colours" functionality

View File

@@ -1,7 +1,6 @@
#! /usr/bin/env bash
./build.sh
rm -rf dist
mkdir dist
cp -r README.md LICENSE index.html script.js background.png pkg includes dist
# butler push dist ruin/pixsy:html
cp -r README.md LICENSE index.html script.js pkg includes old dist
butler push dist ruin/pixsy:html

View File

@@ -1,122 +0,0 @@
.slider[type='range'] {
-webkit-appearance: none;
margin: 9px 0;
width: 100%
}
.slider[type='range']:focus {
outline: 0
}
.slider[type='range']::-webkit-slider-runnable-track {
cursor: pointer;
height: 5px;
width: 100%;
background: #008ecc;
border: none
}
.slider[type='range']::-webkit-slider-thumb {
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 50%;
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.12);
cursor: pointer;
height: 18px;
width: 18px;
-webkit-appearance: none;
margin-top: -6.5px
}
.slider[type='range']::-moz-range-track {
cursor: pointer;
height: 5px;
width: 100%;
background: #008ecc;
border: none
}
.slider[type='range']::-moz-range-thumb {
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 50%;
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.12);
cursor: pointer;
height: 18px;
width: 18px
}
.slider[type='range']::-ms-track {
cursor: pointer;
height: 5px;
width: 100%;
background: transparent;
border-color: transparent;
border-width: 9px 0;
color: transparent
}
.slider[type='range']::-ms-fill-lower {
background: #008ecc;
border: none
}
.slider[type='range']::-ms-fill-upper {
background: #008ecc;
border: none
}
.slider[type='range']::-ms-thumb {
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 50%;
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.12);
cursor: pointer;
height: 18px;
width: 18px;
margin-top: 0
}
.cropper {
display: inline-block
}
.cropper canvas {
border-radius: 3px
}
.cropper canvas:hover {
cursor: move
}
.cropper-tools {
margin-top: 15px;
text-align: center
}
.cropper-zoom {
display: inline-block
}
.cropper-zoom .slider {
margin: 0 10px;
width: 225px
}
.cropper-zoom .icon {
margin-top: 2px;
font-size: 18px
}
.cropper-zoom .icon:last-child {
font-size: 24px
}
.cropper .icon {
display: inline-block;
width: 1em;
height: 1em;
fill: rgba(0, 0, 0, .54);
vertical-align: middle;
}

File diff suppressed because one or more lines are too long

250
includes/croppie.css Normal file
View File

@@ -0,0 +1,250 @@
.croppie-container {
width: 100%;
height: 100%;
}
.croppie-container .cr-image {
z-index: -1;
position: absolute;
top: 0;
left: 0;
transform-origin: 0 0;
max-height: none;
max-width: none;
}
.croppie-container .cr-boundary {
position: relative;
overflow: hidden;
margin: 0 auto;
z-index: 1;
width: 100%;
height: 100%;
}
.croppie-container .cr-viewport,
.croppie-container .cr-resizer {
position: absolute;
border: 2px solid #fff;
margin: auto;
top: 0;
bottom: 0;
right: 0;
left: 0;
box-shadow: 0 0 2000px 2000px rgba(0, 0, 0, 0.5);
z-index: 0;
}
.croppie-container .cr-resizer {
z-index: 2;
box-shadow: none;
pointer-events: none;
}
.croppie-container .cr-resizer-vertical,
.croppie-container .cr-resizer-horisontal {
position: absolute;
pointer-events: all;
}
.croppie-container .cr-resizer-vertical::after,
.croppie-container .cr-resizer-horisontal::after {
display: block;
position: absolute;
box-sizing: border-box;
border: 1px solid black;
background: #fff;
width: 10px;
height: 10px;
content: '';
}
.croppie-container .cr-resizer-vertical {
bottom: -5px;
cursor: row-resize;
width: 100%;
height: 10px;
}
.croppie-container .cr-resizer-vertical::after {
left: 50%;
margin-left: -5px;
}
.croppie-container .cr-resizer-horisontal {
right: -5px;
cursor: col-resize;
width: 10px;
height: 100%;
}
.croppie-container .cr-resizer-horisontal::after {
top: 50%;
margin-top: -5px;
}
.croppie-container .cr-original-image {
display: none;
}
.croppie-container .cr-vp-circle {
border-radius: 50%;
}
.croppie-container .cr-overlay {
z-index: 1;
position: absolute;
cursor: move;
touch-action: none;
}
.croppie-container .cr-slider-wrap {
width: 75%;
margin: 15px auto;
text-align: center;
}
.croppie-result {
position: relative;
overflow: hidden;
}
.croppie-result img {
position: absolute;
}
.croppie-container .cr-image,
.croppie-container .cr-overlay,
.croppie-container .cr-viewport {
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
/*************************************/
/***** STYLING RANGE INPUT ***********/
/*************************************/
/*http://brennaobrien.com/blog/2014/05/style-input-type-range-in-every-browser.html */
/*************************************/
.cr-slider {
-webkit-appearance: none;
/*removes default webkit styles*/
/*border: 1px solid white; *//*fix for FF unable to apply focus style bug */
width: 300px;
/*required for proper track sizing in FF*/
max-width: 100%;
padding-top: 8px;
padding-bottom: 8px;
background-color: transparent;
}
.cr-slider::-webkit-slider-runnable-track {
width: 100%;
height: 3px;
background: rgba(0, 0, 0, 0.5);
border: 0;
border-radius: 3px;
}
.cr-slider::-webkit-slider-thumb {
-webkit-appearance: none;
border: none;
height: 16px;
width: 16px;
border-radius: 50%;
background: #ddd;
margin-top: -6px;
}
.cr-slider:focus {
outline: none;
}
/*
.cr-slider:focus::-webkit-slider-runnable-track {
background: #ccc;
}
*/
.cr-slider::-moz-range-track {
width: 100%;
height: 3px;
background: rgba(0, 0, 0, 0.5);
border: 0;
border-radius: 3px;
}
.cr-slider::-moz-range-thumb {
border: none;
height: 16px;
width: 16px;
border-radius: 50%;
background: #ddd;
margin-top: -6px;
}
/*hide the outline behind the border*/
.cr-slider:-moz-focusring {
outline: 1px solid white;
outline-offset: -1px;
}
.cr-slider::-ms-track {
width: 100%;
height: 5px;
background: transparent;
/*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */
border-color: transparent;/*leave room for the larger thumb to overflow with a transparent border */
border-width: 6px 0;
color: transparent;/*remove default tick marks*/
}
.cr-slider::-ms-fill-lower {
background: rgba(0, 0, 0, 0.5);
border-radius: 10px;
}
.cr-slider::-ms-fill-upper {
background: rgba(0, 0, 0, 0.5);
border-radius: 10px;
}
.cr-slider::-ms-thumb {
border: none;
height: 16px;
width: 16px;
border-radius: 50%;
background: #ddd;
margin-top:1px;
}
.cr-slider:focus::-ms-fill-lower {
background: rgba(0, 0, 0, 0.5);
}
.cr-slider:focus::-ms-fill-upper {
background: rgba(0, 0, 0, 0.5);
}
/*******************************************/
/***********************************/
/* Rotation Tools */
/***********************************/
.cr-rotate-controls {
position: absolute;
bottom: 5px;
left: 5px;
z-index: 1;
}
.cr-rotate-controls button {
border: 0;
background: none;
}
.cr-rotate-controls i:before {
display: inline-block;
font-style: normal;
font-weight: 900;
font-size: 22px;
}
.cr-rotate-l i:before {
content: '↺';
}
.cr-rotate-r i:before {
content: '↻';
}

1
includes/croppie.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,7 @@
@background: #fff4d9;
@text: #ec6d7d;
@accent: #a3c4ef;
@background: #57506a;
@page-background: #968eb5;
@accent: #ec6d7d;
@text: #464256;
* {
box-sizing: border-box;
@@ -21,6 +22,11 @@ button {
white-space: nowrap;
width: 100%;
&.half {
float: left;
width: 50%;
}
&.pagination:not(.normal) {
position: absolute;
bottom: 5vmin;
@@ -36,6 +42,10 @@ button {
}
}
header * {
color: @accent;
}
h1 {
margin: 0;
}
@@ -112,8 +122,8 @@ textarea {
height: 80vmin;
width: 80vmin;
background-color: @background;
border: 2px solid @accent;
background-color: @page-background;
color: @text;
border-radius: 5vmin;
box-shadow: @accent 1vmin 1vmin;
padding: 5vmin;

View File

@@ -4,28 +4,50 @@ html(lang="en-gb")
meta(charset="utf-8")
title pixsy
link(rel="stylesheet" href="includes/style.css")
link(rel="stylesheet" href="includes/cropper.css")
script(src="includes/cropper.min.js")
link(rel="stylesheet" href="includes/croppie.css")
script(src="includes/croppie.min.js")
body
h1
| pixsy
//img(alt="pixsy" src="includes/pixsy.png")
header
h1 pixsy
p.
convert images to Bitsy rooms
version 0.710.0
|
#[a(href="./old/") old version]
#[a(href="http://tinybird.info/image-to-bitsy/old/" target="_blank") old version]
|
#[a(href="mailto:max@tinybird.info") email]
|
#[a(href="https://twitter.com/synth_ruiner") twitter]
.pages
.page#start
p.
#[b pixsy] is a tool for #[a(href="https://bitsy.org/") Bitsy Game Maker]
that allows you to generate a room from an image and add it to your game.
p.
this version is tested to be compatible with Bitsy version 7.10 and earlier.
later versions may also work fine, but make sure you have a backup of your game data.
p.
#[b pixsy] does not currently work via the Itch desktop program.
if pixsy does not work for you, please try the
#[a(href="http://tinybird.info/image-to-bitsy/old/") old version] instead.
p.
if your image is already the correct size for a bitsy room (128×128),
simply leave the zoom slider at the default setting.
you can draw your room in a pixel-art program and import it here.
p.
full instructions can be found on the
#[a(href="https://ruin.itch.io/pixsy/") itch.io page] -
scroll down to "how to use".
button.normal.pagination.next#new create a new bitsy game
button.normal.pagination.next#load load an existing bitsy game
.page.game-data
h2 game data
input#game(type="file" autocomplete="off")
p.
your game data is available from the #[i game data] window in bitsy,
under the #[i tools] dropdown.
input#game(type="file" accept=".bitsy,.txt" autocomplete="off")
br
textarea#game-data(
@@ -40,7 +62,7 @@ html(lang="en-gb")
.image-container
input#image(type="file" accept="image/*")
#crop
#crop(style="display: none;")
button.pagination.prev previous
button.pagination.next#image-next(disabled=true) next
@@ -68,24 +90,27 @@ html(lang="en-gb")
#new-palette(style="display: none;")
.half
input#colour-background(type="color" value="#2f4ac9")
input#colour-background(type="color" value="#000000")
.half
input#colour-foreground(type="color" value="#8798fe")
input#colour-foreground(type="color" value="#ffffff")
label
input#dither(type="checkbox" checked=true)
input#dither(type="checkbox")
| dither
p (approximates a greyscale effect)
br
button.pagination.prev#back-to-image previous
button.pagination.next#room-next add room
.page.download
h2 download
h2 done!
p#added
textarea#output(autocomplete="off")
br
button#download download
button#clipboard.half copy to clipboard
button#download.half download
button.pagination.prev#add add another image
button.pagination.start#reset start again

View File

@@ -12,10 +12,6 @@ import init, {
set_room_name,
} from './pkg/pixsy.js';
if (typeof WebAssembly !== "object") {
window.location = "./old/"
}
// stolen from https://ourcodeworld.com/articles/read/189/how-to-create-a-file-and-generate-a-download-with-javascript-in-the-browser-without-a-server
function download(filename, text) {
let element = document.createElement('a');
@@ -28,6 +24,18 @@ function download(filename, text) {
document.body.removeChild(element);
}
function copyToClipboard() {
const button = el("clipboard");
el("output").select();
document.execCommand("copy");
button.innerText = "copied!";
setTimeout(() => {
button.innerText = "copy to clipboard";
}, 2000);
}
function el(id) {
return document.getElementById(id);
}
@@ -60,10 +68,15 @@ function readFile(input, callback, type = "text") {
}
async function run() {
if (typeof WebAssembly !== "object") {
window.location = "http://tinybird.info/image-to-bitsy/old/"
}
await init();
const buttonAddImage = el("add");
const buttonBackToImage = el("back-to-image");
const buttonCopyToClipboard = el("clipboard")
const buttonDownload = el("download");
const buttonGameDataProceed = el("game-data-next");
const buttonImageProceed = el("image-next");
@@ -72,6 +85,7 @@ async function run() {
const buttonNewGame = el("new");
const buttonReset = el("reset");
const checkboxDither = el("dither");
const divCroppie = el("crop");
const divNewPalette = el("new-palette");
const inputBrightness = el("brightness");
const inputColourBackground = el("colour-background");
@@ -81,8 +95,11 @@ async function run() {
const textareaGameDataInput = el("game-data");
const textareaGameDataOutput = el("output");
const cropper = new Cropper({ width: 192, height: 192 });
let cropperRendered = false;
const croppie = new Croppie(divCroppie, {
viewport: {width: 128, height: 128, type: 'square'},
boundary: {width: 256, height: 256},
enableZoom: true,
});
// hide all pages except start page
for (let page of document.getElementsByClassName('page')) {
@@ -95,8 +112,12 @@ async function run() {
pageButton.addEventListener('touchend', pagination);
}
// croppie needs to be on screen to work;
// halt pagination until we're finished gathering the results
buttonImageProceed.removeEventListener("click", pagination);
function new_game() {
console.debug(load_default_game());
load_default_game();
textareaGameDataInput.value = output();
checkGameData();
// we don't need to look at the default game data, so skip ahead to the image page
@@ -118,14 +139,12 @@ async function run() {
el("game").addEventListener("change", function() {
readFile(this, function (e) {
textareaGameDataInput.value = e.target.result;
console.log(load_game(e.target.result));
checkGameData();
}, "text");
});
function setPaletteDropdown() {
const palettes = JSON.parse(get_palettes());
console.debug(palettes);
selectPalette.innerHTML = "";
@@ -145,7 +164,9 @@ async function run() {
}
function checkGameData() {
if (textareaGameDataInput.value.length > 0) {
let result = load_game(textareaGameDataInput.value)
if (result === "Loaded game") {
buttonGameDataProceed.removeAttribute("disabled");
setPaletteDropdown();
} else {
@@ -159,21 +180,9 @@ async function run() {
el('image').addEventListener('change', function () {
readFile(this, function (e) {
if ( ! cropperRendered) {
cropper.render("#crop");
cropperRendered = true;
croppie.bind({url: e.target.result, zoom: 0});
divCroppie.style.display = "block";
buttonImageProceed.removeAttribute("disabled");
}
if (load_image(e.target.result) === "128×128") {
// we can't just do `buttonImageProceed.click()`
// because this calls the handleImage() function, which we don't want here
el("page-image").style.display = "none";
el("page-room").style.display = "block";
loadPreview();
} else {
cropper.loadImage(e.target.result);
}
}, "image");
});
@@ -182,8 +191,18 @@ async function run() {
}
function handleImage() {
console.log(load_image(cropper.getCroppedImage()));
croppie.result({
type: "base64",
size: "viewport",
format: "png",
}).then((result) => {
console.log("Loading image: " + load_image(result));
el("page-image").style.display = "none";
el("page-room" ).style.display = "block";
loadPreview();
});
}
buttonImageProceed.addEventListener("click", handleImage);
@@ -224,7 +243,7 @@ async function run() {
});
function addRoom() {
console.log(add_room());
el("added").innerText = add_room();
textareaGameDataOutput.value = output();
}
@@ -235,6 +254,9 @@ async function run() {
download("output.bitsy", textareaGameDataOutput.value);
}
buttonCopyToClipboard.addEventListener("click", copyToClipboard);
buttonCopyToClipboard.addEventListener("touchend", copyToClipboard);
buttonDownload.addEventListener("click", handleDownload);
buttonDownload.addEventListener("touchend", handleDownload);
@@ -255,8 +277,8 @@ async function run() {
inputRoomName.value = "";
selectPalette.innerHTML = "";
divNewPalette.style.display = "none";
inputColourBackground.value = "#2f4ac9";
inputColourForeground.value = "#8798fe";
inputColourBackground.value = "#000000";
inputColourForeground.value = "#ffffff";
checkboxDither.checked = true;
}

View File

@@ -1,5 +1,3 @@
#![feature(clamp)]
use bitsy_parser::game::Game;
use bitsy_parser::image::Image;
use bitsy_parser::tile::Tile;
@@ -43,7 +41,7 @@ lazy_static! {
image: None,
room_name: None,
palette: SelectedPalette::None,
dither: true,
dither: false,
brightness: 0,
}
);
@@ -95,6 +93,11 @@ pub fn load_image(image_base64: String) -> String {
let mut state = STATE.lock().expect("Couldn't lock application state");
let image_base64: Vec<&str> = image_base64.split("base64,").collect();
if image_base64.len() < 2 {
return format!("Error: Badly-formatted base64: {}", image_base64.join(""));
}
let image_base64 = image_base64[1];
match base64::decode(image_base64) {
@@ -109,13 +112,13 @@ pub fn load_image(image_base64: String) -> String {
},
_ => {
state.image = None;
"Couldn't load image".to_string()
"Error: Couldn't load image".to_string()
}
}
},
_ => {
state.image = None;
"Couldn't decode image".to_string()
"Error: Couldn't decode image".to_string()
}
}
}
@@ -185,19 +188,23 @@ fn image_to_base64(image: &DynamicImage) -> String {
format!("data:image/png;base64,{}", base64::encode(&bytes))
}
fn palette_from(bg: &bitsy_parser::Colour, fg: &bitsy_parser::Colour) -> bitsy_parser::Palette {
bitsy_parser::Palette {
id: "0".to_string(),
name: None,
colours: vec![
bg.clone(), fg.clone(), bitsy_parser::Colour { red: 0, green: 0, blue: 0 }
],
}
}
fn render_preview(state: &State) -> DynamicImage {
let mut buffer = state.image.as_ref().unwrap().clone().into_rgba();
let mut buffer = state.image.as_ref().unwrap().clone().into_rgba8();
let palette = match &state.palette {
SelectedPalette::None => bitsy_parser::mock::game_default().palettes[0].clone(),
SelectedPalette::Existing { id } => state.game.as_ref().unwrap().get_palette(id).unwrap().clone(),
SelectedPalette::New { background, foreground } => bitsy_parser::Palette {
id: "0".to_string(),
name: None,
colours: vec![
background.clone(), foreground.clone(), bitsy_parser::Colour { red: 0, green: 0, blue: 0 }
],
},
SelectedPalette::New { background, foreground } => palette_from(background, foreground),
};
let colour_map = crate::ColourMap::from(&palette);
@@ -240,33 +247,25 @@ pub fn add_room() -> String {
let mut state = STATE.lock().expect("Couldn't lock application state");
if state.game.is_none() {
return "No game data loaded".to_string();
return "No game data loaded".into();
}
match &state.palette {
SelectedPalette::None => { return "No palette selected".to_string(); },
SelectedPalette::None => { return "No palette selected".into(); },
_ => {}
};
let mut game = state.game.clone().unwrap();
if state.image.is_none() {
return "No image loaded".to_string();
return "No image loaded".into();
}
let palette_id = Some(match &state.palette {
SelectedPalette::None => bitsy_parser::mock::game_default().palettes[0].id.clone(),
SelectedPalette::Existing { id } => id.clone(),
SelectedPalette::New { background, foreground } => {
game.add_palette(bitsy_parser::Palette {
id: "0".to_string(),
name: None,
colours: vec![
background.clone(),
foreground.clone(),
bitsy_parser::Colour { red: 0, green: 0, blue: 0 }
],
})
game.add_palette(palette_from(background, foreground))
},
});
@@ -291,7 +290,7 @@ pub fn add_room() -> String {
fn colour_match(a: &image::Rgb<u8>, b: &bitsy_parser::Colour) -> u8 {
if a[0] == b.red && a[1] == b.green && a[2] == b.blue { 1 } else { 0 }
};
}
for y in (row * SD)..((row + 1) * SD) {
for x in (column * SD)..((column + 1) * SD) {
@@ -319,9 +318,6 @@ pub fn add_room() -> String {
}
}
// todo if player selected "create new game", delete room 0 here?
// that would probably break unless the avatar was also placed in the room
game.add_room(bitsy_parser::Room {
id: "0".to_string(),
palette_id,
@@ -381,7 +377,7 @@ mod test {
load_default_game();
load_image(include_str!("test-resources/colour_input.png.base64").trim().to_string());
let output = get_preview();
let expected = include_str!("test-resources/colour_input.png.base64.greyscale").trim();
let expected = include_str!("test-resources/colour_input.png.base64.blueprint").trim();
assert_eq!(output, expected);
}

View File

@@ -0,0 +1 @@
data:image/png;base64,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

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
Write your game's title here
# BITSY VERSION 7.2
# BITSY VERSION 7.5
! ROOM_FORMAT 1