Added large canvas block & canvas image export/import

This commit is contained in:
Anuken
2026-03-15 00:26:40 -04:00
parent c920fe2d21
commit 26175c65b3
13 changed files with 123 additions and 10 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

View File

@@ -125,6 +125,8 @@ maps.none = [lightgray]No maps found!
invalid = Invalid
pickcolor = Pick Color
color = Color
import = Import
export = Export
preparingconfig = Preparing Config
preparingcontent = Preparing Content
uploadingcontent = Uploading Content
@@ -2068,6 +2070,7 @@ block.reinforced-payload-router.name = Reinforced Payload Router
block.payload-mass-driver.name = Payload Mass Driver
block.small-deconstructor.name = Deconstructor
block.canvas.name = Canvas
block.large-canvas.name = Large Canvas
block.world-processor.name = World Processor
block.world-cell.name = World Cell
block.tank-fabricator.name = Tank Fabricator
@@ -2527,6 +2530,7 @@ block.unit-repair-tower.description = Repairs all units in its vicinity. Require
block.radar.description = Gradually uncovers terrain and enemy units in a large radius. Requires power.
block.shockwave-tower.description = Damages and destroys enemy projectiles in a radius. Requires cyanogen.
block.canvas.description = Displays a simple image with a pre-defined palette. Editable.
block.large-canvas.description = Displays a simple image with a pre-defined palette. Editable.
unit.dagger.description = Fires standard bullets at enemy targets.
unit.mace.description = Fires streams of flame at enemy targets.

View File

@@ -623,3 +623,4 @@
63059=metal-tiles-13|block-metal-tiles-13-ui
63058=metal-wall-3|block-metal-wall-3-ui
63057=ore-wall-graphite|block-ore-wall-graphite-ui
63056=large-canvas|block-large-canvas-ui

Binary file not shown.

View File

@@ -176,7 +176,7 @@ public class Blocks{
//logic
message, switchBlock, microProcessor, logicProcessor, hyperProcessor, largeLogicDisplay, logicDisplay, tileLogicDisplay, memoryCell, memoryBank,
canvas, reinforcedMessage,
canvas, largeCanvas, reinforcedMessage,
worldProcessor, worldCell, worldMessage, worldSwitch,
//campaign
@@ -6882,6 +6882,35 @@ public class Blocks{
size = 2;
}};
largeCanvas = new CanvasBlock("large-canvas"){{
requirements(Category.logic, BuildVisibility.shown, with(Items.silicon, 15, Items.beryllium, 15, Items.tungsten, 10));
canvasSize = 24;
padding = 7f / 4f * 2f;
size = 3;
//Palette: https://lospec.com/palette-list/woodspark
setPaletteFromString("""
452b3f
8a5865
e08d51
fabf61
f5eeb0
2c5e3b
609c4f
c6cc54
78c2d6
5479b0
56546e
839fa6
e0d3c8
f05b5b
8f325f
eb6c98
""");
}};
reinforcedMessage = new MessageBlock("reinforced-message"){{
requirements(Category.logic, with(Items.graphite, 10, Items.beryllium, 5));
health = 100;

View File

@@ -1,6 +1,7 @@
package mindustry.ui.dialogs;
import arc.*;
import arc.files.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.input.*;
@@ -23,22 +24,28 @@ public class CanvasEditDialog extends BaseDialog{
static final float refreshTime = 60f * 2f;
int curColor;
boolean fill, modified;
boolean fill, modified, grid = true;
float time;
CanvasBuild canvas;
CanvasBlock block;
Pixmap pix;
Texture texture;
public CanvasEditDialog(CanvasBuild canvas){
super("");
titleTable.remove();
this.canvas = canvas;
CanvasBlock block = (CanvasBlock)canvas.block;
block = (CanvasBlock)canvas.block;
int size = block.canvasSize;
pix = block.makePixmap(canvas.data, new Pixmap(size, size));
Texture texture = new Texture(pix);
texture = new Texture(pix);
curColor = block.palette[0];
addCloseButton();
addCloseButton(160f);
buttons.button("@import", Icon.image, () -> platform.showFileChooser(true, "png", this::importFrom));
buttons.button("@export", Icon.export, () -> platform.showFileChooser(false, "png", this::exportTo));
hidden(() -> {
save();
@@ -138,7 +145,7 @@ public class CanvasEditDialog extends BaseDialog{
Draw.rect(Tmp.tr1, x + width/2f, y + height/2f, width, height);
//draw grid
{
if(grid){
float xspace = (getWidth() / size);
float yspace = (getHeight() / size);
float s = 1f;
@@ -175,12 +182,18 @@ public class CanvasEditDialog extends BaseDialog{
cont.row();
cont.add().size(60f);
cont.table(Tex.button, t -> {
t.button(Icon.grid, Styles.clearNoneTogglei, () -> grid = !grid).checked(grid).size(44f);
});
cont.table(Tex.button, p -> {
for(int i = 0; i < block.palette.length; i++){
int fi = i;
if(i % 8 == 0){
p.row();
}
var button = p.button(Tex.whiteui, Styles.squareTogglei, 30, () -> {
curColor = block.palette[fi];
}).size(44).checked(b -> curColor == block.palette[fi]).get();
@@ -192,11 +205,69 @@ public class CanvasEditDialog extends BaseDialog{
t.button(Icon.fill, Styles.clearNoneTogglei, () -> fill = !fill).size(44f);
});
closeOnBack();
buttons.defaults().size(150f, 64f);
}
void exportTo(Fi file){
try{
file.writePng(pix);
}catch(Exception e){
ui.showException(e);
}
}
void importFrom(Fi file){
try{
Pixmap source = new Pixmap(file);
int size = pix.width;
if(source.width > size || source.height > size){
float ratio = (float)Math.max(source.width, source.height) / size;
Pixmap dest = new Pixmap(size, size);
dest.draw(source, 0, 0, source.width, source.height, (size - (int)(source.width / ratio))/2, (size - (int)(source.height / ratio))/2, (int)(source.width / ratio), (int)(source.height / ratio));
source.dispose();
source = dest;
}else if(source.width < size || source.height < size){
pix.fill(block.palette[0]);
Pixmap dest = new Pixmap(size, size);
dest.draw(source, (size - source.width)/2, (size - source.height)/2);
source.dispose();
source = dest;
}
int sizeX = Math.min(source.width, pix.width), sizeY = Math.min(source.height, pix.height);
for(int x = 0; x < sizeX; x++){
for(int y = 0; y < sizeY; y++){
int c = source.getRaw(x, y);
pix.setRaw(x, y, findClosest(c));
}
}
texture.draw(pix);
modified = true;
}catch(Exception e){
ui.showException("@editor.errorload", e);
}
}
int findClosest(int color){
//blend the new color over the bg color for more accurate selection
if(Color.ai(color) < 255){
color = Pixmap.blend(block.palette[0], color);
}
Tmp.c1.set(color);
float nearestDst = 100f;
int nearest = 0;
for(int i = 0; i < block.palette.length; i++){
if(block.palette[i] == color) return color;
Tmp.c2.set(block.palette[i]);
float dst = Tmp.c1.dst(Tmp.c2);
if(dst < nearestDst){
nearest = i;
nearestDst = dst;
}
}
return block.palette[nearest];
}
void save(){
if(modified){
canvas.configure(canvas.packPixmap(pix));

View File

@@ -51,6 +51,14 @@ public class CanvasBlock extends Block{
});
}
public void setPaletteFromString(String value){
String[] split = value.split("\n");
palette = new int[split.length];
for(int i = 0; i < split.length; i++){
palette[i] = (Integer.parseInt(split[i], 16) << 8) | 0xff;
}
}
@Override
public void init(){
super.init();

View File

@@ -26,4 +26,4 @@ org.gradle.caching=true
org.gradle.internal.http.socketTimeout=100000
org.gradle.internal.http.connectionTimeout=100000
android.enableR8.fullMode=false
archash=1abdd6e4bf
archash=900296c6e8