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 invalid = Invalid
pickcolor = Pick Color pickcolor = Pick Color
color = Color color = Color
import = Import
export = Export
preparingconfig = Preparing Config preparingconfig = Preparing Config
preparingcontent = Preparing Content preparingcontent = Preparing Content
uploadingcontent = Uploading 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.payload-mass-driver.name = Payload Mass Driver
block.small-deconstructor.name = Deconstructor block.small-deconstructor.name = Deconstructor
block.canvas.name = Canvas block.canvas.name = Canvas
block.large-canvas.name = Large Canvas
block.world-processor.name = World Processor block.world-processor.name = World Processor
block.world-cell.name = World Cell block.world-cell.name = World Cell
block.tank-fabricator.name = Tank Fabricator 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.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.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.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.dagger.description = Fires standard bullets at enemy targets.
unit.mace.description = Fires streams of flame 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 63059=metal-tiles-13|block-metal-tiles-13-ui
63058=metal-wall-3|block-metal-wall-3-ui 63058=metal-wall-3|block-metal-wall-3-ui
63057=ore-wall-graphite|block-ore-wall-graphite-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 //logic
message, switchBlock, microProcessor, logicProcessor, hyperProcessor, largeLogicDisplay, logicDisplay, tileLogicDisplay, memoryCell, memoryBank, message, switchBlock, microProcessor, logicProcessor, hyperProcessor, largeLogicDisplay, logicDisplay, tileLogicDisplay, memoryCell, memoryBank,
canvas, reinforcedMessage, canvas, largeCanvas, reinforcedMessage,
worldProcessor, worldCell, worldMessage, worldSwitch, worldProcessor, worldCell, worldMessage, worldSwitch,
//campaign //campaign
@@ -6882,6 +6882,35 @@ public class Blocks{
size = 2; 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"){{ reinforcedMessage = new MessageBlock("reinforced-message"){{
requirements(Category.logic, with(Items.graphite, 10, Items.beryllium, 5)); requirements(Category.logic, with(Items.graphite, 10, Items.beryllium, 5));
health = 100; health = 100;

View File

@@ -1,6 +1,7 @@
package mindustry.ui.dialogs; package mindustry.ui.dialogs;
import arc.*; import arc.*;
import arc.files.*;
import arc.graphics.*; import arc.graphics.*;
import arc.graphics.g2d.*; import arc.graphics.g2d.*;
import arc.input.*; import arc.input.*;
@@ -23,22 +24,28 @@ public class CanvasEditDialog extends BaseDialog{
static final float refreshTime = 60f * 2f; static final float refreshTime = 60f * 2f;
int curColor; int curColor;
boolean fill, modified; boolean fill, modified, grid = true;
float time; float time;
CanvasBuild canvas; CanvasBuild canvas;
CanvasBlock block;
Pixmap pix; Pixmap pix;
Texture texture;
public CanvasEditDialog(CanvasBuild canvas){ public CanvasEditDialog(CanvasBuild canvas){
super(""); super("");
titleTable.remove(); titleTable.remove();
this.canvas = canvas; this.canvas = canvas;
CanvasBlock block = (CanvasBlock)canvas.block; block = (CanvasBlock)canvas.block;
int size = block.canvasSize; int size = block.canvasSize;
pix = block.makePixmap(canvas.data, new Pixmap(size, size)); pix = block.makePixmap(canvas.data, new Pixmap(size, size));
Texture texture = new Texture(pix); texture = new Texture(pix);
curColor = block.palette[0]; 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(() -> { hidden(() -> {
save(); save();
@@ -138,7 +145,7 @@ public class CanvasEditDialog extends BaseDialog{
Draw.rect(Tmp.tr1, x + width/2f, y + height/2f, width, height); Draw.rect(Tmp.tr1, x + width/2f, y + height/2f, width, height);
//draw grid //draw grid
{ if(grid){
float xspace = (getWidth() / size); float xspace = (getWidth() / size);
float yspace = (getHeight() / size); float yspace = (getHeight() / size);
float s = 1f; float s = 1f;
@@ -175,12 +182,18 @@ public class CanvasEditDialog extends BaseDialog{
cont.row(); 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 -> { cont.table(Tex.button, p -> {
for(int i = 0; i < block.palette.length; i++){ for(int i = 0; i < block.palette.length; i++){
int fi = i; int fi = i;
if(i % 8 == 0){
p.row();
}
var button = p.button(Tex.whiteui, Styles.squareTogglei, 30, () -> { var button = p.button(Tex.whiteui, Styles.squareTogglei, 30, () -> {
curColor = block.palette[fi]; curColor = block.palette[fi];
}).size(44).checked(b -> curColor == block.palette[fi]).get(); }).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); t.button(Icon.fill, Styles.clearNoneTogglei, () -> fill = !fill).size(44f);
}); });
closeOnBack();
buttons.defaults().size(150f, 64f); 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(){ void save(){
if(modified){ if(modified){
canvas.configure(canvas.packPixmap(pix)); 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 @Override
public void init(){ public void init(){
super.init(); super.init();

View File

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