mirror of
https://github.com/badvision/lawless-legends.git
synced 2025-02-09 09:31:57 +00:00
Quantum leap forward with a new image conversion wizard!
This commit is contained in:
parent
c43f381296
commit
f43c642875
@ -7,7 +7,6 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
@ -46,7 +45,6 @@ public class MythosEditor {
|
|||||||
|
|
||||||
public void show() {
|
public void show() {
|
||||||
primaryStage = new Stage();
|
primaryStage = new Stage();
|
||||||
|
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/MythosScriptEditor.fxml"));
|
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/MythosScriptEditor.fxml"));
|
||||||
Map<String, String> properties = new HashMap<>();
|
Map<String, String> properties = new HashMap<>();
|
||||||
properties.put(MythosScriptEditorController.ONLOAD_SCRIPT, generateLoadScript());
|
properties.put(MythosScriptEditorController.ONLOAD_SCRIPT, generateLoadScript());
|
||||||
@ -61,22 +59,16 @@ public class MythosEditor {
|
|||||||
throw new RuntimeException(exception);
|
throw new RuntimeException(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
|
primaryStage.setOnCloseRequest((final WindowEvent t) -> {
|
||||||
@Override
|
|
||||||
public void handle(final WindowEvent t) {
|
|
||||||
t.consume();
|
t.consume();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
javafx.application.Platform.runLater(new Runnable() {
|
javafx.application.Platform.runLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
primaryStage.getScene().getRoot().setDisable(true);
|
primaryStage.getScene().getRoot().setDisable(true);
|
||||||
primaryStage.close();
|
primaryStage.close();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import java.util.Map;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Menu;
|
import javafx.scene.control.Menu;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.image.WritableImage;
|
import javafx.scene.image.WritableImage;
|
||||||
@ -23,7 +22,6 @@ import org.badvision.outlaweditor.FileUtils;
|
|||||||
import org.badvision.outlaweditor.ImageEditor;
|
import org.badvision.outlaweditor.ImageEditor;
|
||||||
import org.badvision.outlaweditor.Platform;
|
import org.badvision.outlaweditor.Platform;
|
||||||
import org.badvision.outlaweditor.ui.UIAction;
|
import org.badvision.outlaweditor.ui.UIAction;
|
||||||
import org.badvision.outlaweditor.data.DataObserver;
|
|
||||||
import org.badvision.outlaweditor.data.TileMap;
|
import org.badvision.outlaweditor.data.TileMap;
|
||||||
import org.badvision.outlaweditor.data.xml.Image;
|
import org.badvision.outlaweditor.data.xml.Image;
|
||||||
import org.badvision.outlaweditor.data.xml.PlatformData;
|
import org.badvision.outlaweditor.data.xml.PlatformData;
|
||||||
@ -65,11 +63,8 @@ public class AppleImageEditor extends ImageEditor implements EventHandler<MouseE
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void buildPatternSelector(Menu tilePatternMenu) {
|
public void buildPatternSelector(Menu tilePatternMenu) {
|
||||||
FillPattern.buildMenu(tilePatternMenu, new DataObserver<FillPattern>() {
|
FillPattern.buildMenu(tilePatternMenu, (FillPattern object) -> {
|
||||||
@Override
|
|
||||||
public void observedObjectChanged(FillPattern object) {
|
|
||||||
changeCurrentPattern(object);
|
changeCurrentPattern(object);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,16 +136,14 @@ public class AppleImageEditor extends ImageEditor implements EventHandler<MouseE
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void togglePanZoom() {
|
public void togglePanZoom() {
|
||||||
for (Node n : anchorPane.getChildren()) {
|
anchorPane.getChildren().stream().filter((n) -> !(n == screen)).forEach((n) -> {
|
||||||
if (n == screen) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
n.setVisible(!n.isVisible());
|
n.setVisible(!n.isVisible());
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -393,9 +386,10 @@ public class AppleImageEditor extends ImageEditor implements EventHandler<MouseE
|
|||||||
if (pasteAppContent((String) Clipboard.getSystemClipboard().getContent(DataFormat.PLAIN_TEXT))) {
|
if (pasteAppContent((String) Clipboard.getSystemClipboard().getContent(DataFormat.PLAIN_TEXT))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
if (Clipboard.getSystemClipboard().hasContent(DataFormat.IMAGE)) {
|
if (Clipboard.getSystemClipboard().hasContent(DataFormat.IMAGE)) {
|
||||||
javafx.scene.image.Image image = Clipboard.getSystemClipboard().getImage();
|
javafx.scene.image.Image image = Clipboard.getSystemClipboard().getImage();
|
||||||
|
|
||||||
importImage(image);
|
importImage(image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -435,13 +429,9 @@ public class AppleImageEditor extends ImageEditor implements EventHandler<MouseE
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void importImage(javafx.scene.image.Image image) {
|
private void importImage(javafx.scene.image.Image image) {
|
||||||
FloydSteinbergDither.floydSteinbergDither(image, getPlatform(), 0, 0, getWidth(), getHeight(), getImageData(), getWidth(), new FloydSteinbergDither.DitherCallback() {
|
FloydSteinbergDither ditherEngine = new FloydSteinbergDither(getPlatform());
|
||||||
@Override
|
ditherEngine.setTargetCoordinates(0,0);
|
||||||
public void ditherCompleted(byte[] data) {
|
UIAction.openImageConversionModal(image, ditherEngine, getWidth(), getHeight(), this::setData);
|
||||||
setData(data);
|
|
||||||
redraw();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculateHiresOffset(int y) {
|
private int calculateHiresOffset(int y) {
|
||||||
@ -478,17 +468,10 @@ public class AppleImageEditor extends ImageEditor implements EventHandler<MouseE
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resize(final int newWidth, final int newHeight) {
|
public void resize(final int newWidth, final int newHeight) {
|
||||||
UIAction.confirm("Do you want to scale the image? If you select no, the image will be cropped as needed.",
|
UIAction.confirm("Do you want to scale the image? If you select no, the image will be cropped as needed.", () -> {
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
rescale(newWidth, newHeight);
|
rescale(newWidth, newHeight);
|
||||||
}
|
}, () -> {
|
||||||
}, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
crop(newWidth, newHeight);
|
crop(newWidth, newHeight);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,22 +5,17 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.SnapshotParameters;
|
import javafx.scene.SnapshotParameters;
|
||||||
import javafx.scene.canvas.Canvas;
|
import javafx.scene.canvas.Canvas;
|
||||||
import javafx.scene.effect.DropShadow;
|
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.image.PixelFormat;
|
import javafx.scene.image.PixelFormat;
|
||||||
import javafx.scene.image.PixelReader;
|
import javafx.scene.image.PixelReader;
|
||||||
import javafx.scene.image.PixelWriter;
|
import javafx.scene.image.PixelWriter;
|
||||||
import javafx.scene.image.WritableImage;
|
import javafx.scene.image.WritableImage;
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.text.Font;
|
import javax.swing.Renderer;
|
||||||
import javafx.scene.text.Text;
|
import org.badvision.outlaweditor.Platform;
|
||||||
import javafx.stage.Stage;
|
|
||||||
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.hgrToDhgr;
|
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.hgrToDhgr;
|
||||||
|
|
||||||
/* Copyright (c) 2013 the authors listed at the following URL, and/or
|
/* Copyright (c) 2013 the authors listed at the following URL, and/or
|
||||||
@ -53,106 +48,104 @@ import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.hgrToDhgr;
|
|||||||
*/
|
*/
|
||||||
public class FloydSteinbergDither {
|
public class FloydSteinbergDither {
|
||||||
|
|
||||||
static final int totalPasses = 1;
|
int byteRenderWidth;
|
||||||
static final int nonErrorPasses = 1;
|
|
||||||
|
|
||||||
public static interface DitherCallback {
|
|
||||||
|
|
||||||
public void ditherCompleted(byte[] data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void floydSteinbergDither(
|
|
||||||
Image img,
|
|
||||||
final org.badvision.outlaweditor.Platform platform,
|
|
||||||
final int startX,
|
|
||||||
final int startY,
|
|
||||||
final int width,
|
|
||||||
final int height,
|
|
||||||
final byte[] screen,
|
|
||||||
final int bufferWidth,
|
|
||||||
final DitherCallback callback) {
|
|
||||||
final AppleImageRenderer renderer = (AppleImageRenderer) platform.imageRenderer;
|
|
||||||
final int errorWindow = 6;
|
final int errorWindow = 6;
|
||||||
final int overlap = 2;
|
final int overlap = 2;
|
||||||
final int pixelShift = -2;
|
final int pixelShift = -2;
|
||||||
int byteRenderWidth = platform == org.badvision.outlaweditor.Platform.AppleII_DHGR ? 7 : 14;
|
WritableImage source;
|
||||||
final WritableImage source = getScaledImage(img, width * byteRenderWidth, height);
|
byte[] screen;
|
||||||
AnchorPane pane = new AnchorPane();
|
Platform platform;
|
||||||
Scene s = new Scene(pane);
|
int bufferWidth;
|
||||||
final ImageView previewImage = new ImageView(source);
|
int height;
|
||||||
previewImage.setLayoutX(0);
|
int divisor;
|
||||||
previewImage.setLayoutY(0);
|
int[][] coefficients;
|
||||||
pane.getChildren().add(previewImage);
|
boolean resetOutput = true;
|
||||||
final Text status = new Text("Status");
|
|
||||||
status.setLayoutX(100);
|
|
||||||
status.setLayoutY(40);
|
|
||||||
status.setFont(Font.font("Arial", 18));
|
|
||||||
status.setStroke(Color.BLACK);
|
|
||||||
status.setEffect(new DropShadow(10.0, 0, 0, Color.WHITE));
|
|
||||||
pane.getChildren().add(status);
|
|
||||||
Stage progress = new Stage();
|
|
||||||
progress.setScene(s);
|
|
||||||
progress.show();
|
|
||||||
|
|
||||||
Thread t = new Thread(new Runnable() {
|
public FloydSteinbergDither(org.badvision.outlaweditor.Platform platform) {
|
||||||
@Override
|
this.platform = platform;
|
||||||
public void run() {
|
byteRenderWidth = platform == org.badvision.outlaweditor.Platform.AppleII_DHGR ? 7 : 14;
|
||||||
final WritableImage keepScaled = new WritableImage(source.getPixelReader(), 560, 192);
|
}
|
||||||
WritableImage tmpScaled = new WritableImage(source.getPixelReader(), 560, 192);
|
|
||||||
for (int i = 0; i < screen.length; i++) {
|
public void setSourceImage(Image img) {
|
||||||
screen[i] = (byte) Math.max(255, Math.random() * 256.0);
|
source = getScaledImage(img, bufferWidth * byteRenderWidth, height);
|
||||||
|
resetOutput = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WritableImage getScaledImage(Image img, int width, int height) {
|
||||||
|
Canvas c = new Canvas(width, height);
|
||||||
|
c.getGraphicsContext2D().drawImage(img, 0, 0, width, height);
|
||||||
|
WritableImage newImg = new WritableImage(width, height);
|
||||||
|
SnapshotParameters sp = new SnapshotParameters();
|
||||||
|
c.snapshot(sp, newImg);
|
||||||
|
return newImg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WritableImage getPreviewImage() {
|
||||||
|
return new WritableImage(bufferWidth * byteRenderWidth, height*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDimensions(int width, int height) {
|
||||||
|
this.bufferWidth = width;
|
||||||
|
this.height = height;
|
||||||
|
screen = platform.imageRenderer.createImageBuffer(width, height);
|
||||||
|
resetOutput = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDivisor(int divisor) {
|
||||||
|
this.divisor = divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCoefficients(int[][] coefficients) {
|
||||||
|
this.coefficients = coefficients;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startX;
|
||||||
|
int startY;
|
||||||
|
|
||||||
|
public void setTargetCoordinates(int x, int y) {
|
||||||
|
startX = x;
|
||||||
|
startY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
WritableImage keepScaled;
|
||||||
|
WritableImage tmpScaled;
|
||||||
|
int[] scanline;
|
||||||
|
List<Integer> pixels;
|
||||||
|
|
||||||
|
public void restartDither() {
|
||||||
|
keepScaled = new WritableImage(source.getPixelReader(), 560, 192);
|
||||||
|
tmpScaled = new WritableImage(source.getPixelReader(), 560, 192);
|
||||||
|
for (int i = 0; i < screen.length; i++) {
|
||||||
|
screen[i]=(byte) 255;
|
||||||
|
// screen[i] = (byte) Math.max(255, Math.random() * 256.0);
|
||||||
|
}
|
||||||
|
scanline = new int[3];
|
||||||
|
pixels = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] dither(boolean propagateError) {
|
||||||
|
if (resetOutput) {
|
||||||
|
restartDither();
|
||||||
|
resetOutput = false;
|
||||||
}
|
}
|
||||||
// Platform.runLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public void run() {
|
|
||||||
// previewImage.setCache(false);
|
|
||||||
// previewImage.setImage(keepScaled);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// try {
|
|
||||||
// while (previewImage.getImage() != keepScaled) {
|
|
||||||
// Thread.sleep(10);
|
|
||||||
// }
|
|
||||||
// } catch (InterruptedException ex) {
|
|
||||||
// Logger.getLogger(FloydSteinbergDither.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
// }
|
|
||||||
int[] scanline = new int[3];
|
|
||||||
List<Integer> pixels = new ArrayList<>();
|
|
||||||
for (int pass = 0; pass < totalPasses; pass++) {
|
|
||||||
keepScaled.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0);
|
keepScaled.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0);
|
||||||
tmpScaled.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0);
|
tmpScaled.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0);
|
||||||
final String statusText = "Pass: " + (pass + 1) + " of " + totalPasses;
|
|
||||||
Platform.runLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
status.setText(statusText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
System.out.println("Image type: " + platform.name());
|
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < width; x += 2) {
|
for (int x = 0; x < bufferWidth; x += 2) {
|
||||||
Thread.yield();
|
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case AppleII:
|
case AppleII:
|
||||||
hiresDither(screen, y, x, scanline, pixels, tmpScaled, pass, keepScaled);
|
hiresDither(y, x, propagateError, keepScaled);
|
||||||
break;
|
break;
|
||||||
case AppleII_DHGR:
|
case AppleII_DHGR:
|
||||||
doubleHiresDither(screen, y, x, scanline, pixels, tmpScaled, pass, keepScaled);
|
doubleHiresDither(y, x, propagateError, keepScaled);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.runLater(new Runnable() {
|
return screen;
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.ditherCompleted(screen);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
status.setText("Complete!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hiresDither(final byte[] screen, int y, int x, int[] scanline, List<Integer> pixels, WritableImage tmpScaled, int pass, final WritableImage keepScaled) {
|
void hiresDither(int y, int x, boolean propagateError, final WritableImage keepScaled) {
|
||||||
int bb1 = screen[(y + startY) * bufferWidth + startX + x] & 255;
|
int bb1 = screen[(y + startY) * bufferWidth + startX + x] & 255;
|
||||||
int bb2 = screen[(y + startY) * bufferWidth + startX + x + 1] & 255;
|
int bb2 = screen[(y + startY) * bufferWidth + startX + x + 1] & 255;
|
||||||
int next = bb2 & 127; // Preserve hi-bit so last pixel stays solid, it is a very minor detail
|
int next = bb2 & 127; // Preserve hi-bit so last pixel stays solid, it is a very minor detail
|
||||||
@ -203,9 +196,9 @@ public class FloydSteinbergDither {
|
|||||||
col1 = Palette.parseIntColor(on1);
|
col1 = Palette.parseIntColor(on1);
|
||||||
col2 = Palette.parseIntColor(on2);
|
col2 = Palette.parseIntColor(on2);
|
||||||
}
|
}
|
||||||
if (pass >= nonErrorPasses) {
|
if (propagateError) {
|
||||||
propagateError(x * 14 + c * 2, y, tmpScaled, col1, true, false);
|
propagateError(x * 14 + c * 2, y, tmpScaled, col1);
|
||||||
propagateError(x * 14 + c * 2 + 1, y, tmpScaled, col2, false, true);
|
propagateError(x * 14 + c * 2 + 1, y, tmpScaled, col2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (totalError < leastError) {
|
if (totalError < leastError) {
|
||||||
@ -252,9 +245,9 @@ public class FloydSteinbergDither {
|
|||||||
col1 = Palette.parseIntColor(on1);
|
col1 = Palette.parseIntColor(on1);
|
||||||
col2 = Palette.parseIntColor(on2);
|
col2 = Palette.parseIntColor(on2);
|
||||||
}
|
}
|
||||||
if (pass >= nonErrorPasses) {
|
if (propagateError) {
|
||||||
propagateError(x * 14 + c * 2 + 14, y, tmpScaled, col1, true, false);
|
propagateError(x * 14 + c * 2 + 14, y, tmpScaled, col1);
|
||||||
propagateError(x * 14 + c * 2 + 15, y, tmpScaled, col2, false, true);
|
propagateError(x * 14 + c * 2 + 15, y, tmpScaled, col2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (totalError < leastError) {
|
if (totalError < leastError) {
|
||||||
@ -269,7 +262,7 @@ public class FloydSteinbergDither {
|
|||||||
screen[(y + startY) * bufferWidth + startX + x + 1] = (byte) bb2;
|
screen[(y + startY) * bufferWidth + startX + x + 1] = (byte) bb2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void doubleHiresDither(final byte[] screen, int y, int x, int[] scanline, List<Integer> pixels, WritableImage tmpScaled, int pass, final WritableImage keepScaled) {
|
void doubleHiresDither(int y, int x, boolean propagateError, final WritableImage keepScaled) {
|
||||||
if (x % 4 != 0) {
|
if (x % 4 != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -288,35 +281,35 @@ public class FloydSteinbergDither {
|
|||||||
screen[y * 80 + x + 3] & 255
|
screen[y * 80 + x + 3] & 255
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int xx = 0; xx < 4; xx++) {
|
for (int byteOffset = 0; byteOffset < 4; byteOffset++) {
|
||||||
// First byte, compared with a sliding window encompassing the previous byte, if any.
|
// First byte, compared with a sliding window encompassing the previous byte, if any.
|
||||||
int leastError = Integer.MAX_VALUE;
|
int leastError = Integer.MAX_VALUE;
|
||||||
int b1 = (bytes[xx] & 0x07f);
|
int b1 = (bytes[byteOffset] & 0x07f);
|
||||||
for (int c = 0; c < 7; c++) {
|
for (int bit = 0; bit < 7; bit++) {
|
||||||
int on = b1 | (1 << c);
|
int on = b1 | (1 << bit);
|
||||||
int off = on ^ (1 << c);
|
int off = on ^ (1 << bit);
|
||||||
// get values for "off"
|
// get values for "off"
|
||||||
int i = (xx == 3) ? off : bytes[3] & 255;
|
int i = (byteOffset == 3) ? off : bytes[3] & 255;
|
||||||
i <<= 7;
|
i <<= 7;
|
||||||
i |= (xx == 2) ? off : bytes[2] & 255;
|
i |= (byteOffset == 2) ? off : bytes[2] & 255;
|
||||||
i <<= 7;
|
i <<= 7;
|
||||||
i |= (xx == 1) ? off : bytes[1] & 255;
|
i |= (byteOffset == 1) ? off : bytes[1] & 255;
|
||||||
i <<= 7;
|
i <<= 7;
|
||||||
i |= (xx == 0) ? off : bytes[0] & 255;
|
i |= (byteOffset == 0) ? off : bytes[0] & 255;
|
||||||
scanline[1] = i;
|
scanline[1] = i;
|
||||||
int errorOff = getError((x + xx) * 7 - overlap + c, y, 28 + (xx * 7) + c - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
int errorOff = getError((x + byteOffset) * 7 - overlap + bit, y, 28 + (byteOffset * 7) + bit - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
||||||
int off1 = pixels.get(xx * 7 + c + 28 + pixelShift);
|
int off1 = pixels.get(byteOffset * 7 + bit + 28 + pixelShift);
|
||||||
// get values for "on"
|
// get values for "on"
|
||||||
i = (xx == 3) ? on : bytes[3] & 255;
|
i = (byteOffset == 3) ? on : bytes[3] & 255;
|
||||||
i <<= 7;
|
i <<= 7;
|
||||||
i |= (xx == 2) ? on : bytes[2] & 255;
|
i |= (byteOffset == 2) ? on : bytes[2] & 255;
|
||||||
i <<= 7;
|
i <<= 7;
|
||||||
i |= (xx == 1) ? on : bytes[1] & 255;
|
i |= (byteOffset == 1) ? on : bytes[1] & 255;
|
||||||
i <<= 7;
|
i <<= 7;
|
||||||
i |= (xx == 0) ? on : bytes[0] & 255;
|
i |= (byteOffset == 0) ? on : bytes[0] & 255;
|
||||||
scanline[1] = i;
|
scanline[1] = i;
|
||||||
int errorOn = getError((x + xx) * 7 - overlap + c, y, 28 + (xx * 7) + c - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
int errorOn = getError((x + byteOffset) * 7 - overlap + bit, y, 28 + (byteOffset * 7) + bit - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
||||||
int on1 = pixels.get(xx * 7 + c + 28 + pixelShift);
|
int on1 = pixels.get(byteOffset * 7 + bit + 28 + pixelShift);
|
||||||
|
|
||||||
int[] col1;
|
int[] col1;
|
||||||
if (errorOff < errorOn) {
|
if (errorOff < errorOn) {
|
||||||
@ -328,14 +321,14 @@ public class FloydSteinbergDither {
|
|||||||
b1 = on;
|
b1 = on;
|
||||||
col1 = Palette.parseIntColor(on1);
|
col1 = Palette.parseIntColor(on1);
|
||||||
}
|
}
|
||||||
if (pass >= nonErrorPasses) {
|
if (propagateError) {
|
||||||
propagateError((x + xx) * 7 + c, y, tmpScaled, col1, false, false);
|
propagateError((x + byteOffset) * 7 + bit, y, tmpScaled, col1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if (totalError < leastError) {
|
// if (totalError < leastError) {
|
||||||
keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, tmpScaled.getPixelReader(), 0, y);
|
keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, tmpScaled.getPixelReader(), 0, y);
|
||||||
// leastError = totalError;
|
// leastError = totalError;
|
||||||
bytes[xx] = b1;
|
bytes[byteOffset] = b1;
|
||||||
// } else {
|
// } else {
|
||||||
// tmpScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
|
// tmpScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
|
||||||
}
|
}
|
||||||
@ -344,30 +337,20 @@ public class FloydSteinbergDither {
|
|||||||
screen[(y + startY) * bufferWidth + startX + x + 2] = (byte) bytes[2];
|
screen[(y + startY) * bufferWidth + startX + x + 2] = (byte) bytes[2];
|
||||||
screen[(y + startY) * bufferWidth + startX + x + 3] = (byte) bytes[3];
|
screen[(y + startY) * bufferWidth + startX + x + 3] = (byte) bytes[3];
|
||||||
}
|
}
|
||||||
});
|
|
||||||
t.start();
|
|
||||||
// progress.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void propagateError(int x, int y, WritableImage img, int[] newColor, boolean propagateLeft, boolean propagateRight) {
|
public static int ALPHA_SOLID = 255 << 24;
|
||||||
int solid = 255 << 24;
|
|
||||||
|
private void propagateError(int x, int y, WritableImage img, int[] newColor) {
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
if (x + 1 < img.getWidth() && propagateRight) {
|
|
||||||
int error = Palette.getComponent(img.getPixelReader().getArgb(x + 1, y), i) - newColor[i];
|
|
||||||
int c = img.getPixelReader().getArgb(x + 1, y);
|
|
||||||
img.getPixelWriter().setArgb(x + 1, y, solid | Palette.addError(c, i, (error * 7) >> 4));
|
|
||||||
}
|
|
||||||
if (y + 1 < img.getHeight()) {
|
|
||||||
int error = Palette.getComponent(img.getPixelReader().getArgb(x, y), i) - newColor[i];
|
int error = Palette.getComponent(img.getPixelReader().getArgb(x, y), i) - newColor[i];
|
||||||
if (x - 1 > 0 && propagateLeft) {
|
for (int yy = 0; yy < 3 && y + yy < img.getHeight(); yy++) {
|
||||||
int c = img.getPixelReader().getArgb(x - 1, y + 1);
|
for (int xx = -2; xx < 3 && x + xx < img.getWidth(); xx++) {
|
||||||
img.getPixelWriter().setArgb(x - 1, y + 1, solid | Palette.addError(c, i, (error * 3) >> 4));
|
if (x + xx < 0 || coefficients[xx + 2][yy] == 0) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
int c = img.getPixelReader().getArgb(x, y + 1);
|
int c = img.getPixelReader().getArgb(x + xx, y + yy);
|
||||||
img.getPixelWriter().setArgb(x, y + 1, solid | Palette.addError(c, i, (error * 5) >> 4));
|
int errorAmount = ((error * coefficients[xx+2][yy]) / divisor);
|
||||||
if (x + 1 < img.getWidth() && propagateRight) {
|
img.getPixelWriter().setArgb(x + xx, y + yy, ALPHA_SOLID | Palette.addError(c, i, errorAmount));
|
||||||
c = img.getPixelReader().getArgb(x + 1, y + 1);
|
|
||||||
img.getPixelWriter().setArgb(x + 1, y + 1, solid | Palette.addError(c, i, error >> 4));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -378,7 +361,7 @@ public class FloydSteinbergDither {
|
|||||||
PixelWriter fakeWriter = new PixelWriter() {
|
PixelWriter fakeWriter = new PixelWriter() {
|
||||||
@Override
|
@Override
|
||||||
public PixelFormat getPixelFormat() {
|
public PixelFormat getPixelFormat() {
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -388,34 +371,29 @@ public class FloydSteinbergDither {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setColor(int i, int i1, Color color) {
|
public void setColor(int i, int i1, Color color) {
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends Buffer> void setPixels(int i, int i1, int i2, int i3, PixelFormat<T> pf, T t, int i4) {
|
public <T extends Buffer> void setPixels(int i, int i1, int i2, int i3, PixelFormat<T> pf, T t, int i4) {
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPixels(int i, int i1, int i2, int i3, PixelFormat<ByteBuffer> pf, byte[] bytes, int i4, int i5) {
|
public void setPixels(int i, int i1, int i2, int i3, PixelFormat<ByteBuffer> pf, byte[] bytes, int i4, int i5) {
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPixels(int i, int i1, int i2, int i3, PixelFormat<IntBuffer> pf, int[] ints, int i4, int i5) {
|
public void setPixels(int i, int i1, int i2, int i3, PixelFormat<IntBuffer> pf, int[] ints, int i4, int i5) {
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPixels(int i, int i1, int i2, int i3, PixelReader reader, int i4, int i5) {
|
public void setPixels(int i, int i1, int i2, int i3, PixelReader reader, int i4, int i5) {
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
AppleImageRenderer.renderScanline(fakeWriter, 0, scanline, true, false, 20);
|
AppleImageRenderer.renderScanline(fakeWriter, 0, scanline, true, false, 20);
|
||||||
double max = 0;
|
// double max = 0;
|
||||||
double min = Double.MAX_VALUE;
|
// double min = Double.MAX_VALUE;
|
||||||
double total = 0;
|
double total = 0;
|
||||||
List<Double> err = new ArrayList<>();
|
// List<Double> err = new ArrayList<>();
|
||||||
for (int p = 0; p < window; p++) {
|
for (int p = 0; p < window; p++) {
|
||||||
if ((imageXStart + p) < 0 || (imageXStart + p) >= 560) {
|
if ((imageXStart + p) < 0 || (imageXStart + p) >= 560) {
|
||||||
continue;
|
continue;
|
||||||
@ -424,9 +402,9 @@ public class FloydSteinbergDither {
|
|||||||
int[] c2 = Palette.parseIntColor(source.getArgb(imageXStart + p, y));
|
int[] c2 = Palette.parseIntColor(source.getArgb(imageXStart + p, y));
|
||||||
double dist = Palette.distance(c1, c2);
|
double dist = Palette.distance(c1, c2);
|
||||||
total += dist;
|
total += dist;
|
||||||
max = Math.max(dist, max);
|
// max = Math.max(dist, max);
|
||||||
min = Math.min(dist, min);
|
// min = Math.min(dist, min);
|
||||||
err.add(dist);
|
// err.add(dist);
|
||||||
}
|
}
|
||||||
// double avg = total/((double) window);
|
// double avg = total/((double) window);
|
||||||
// double range = max-min;
|
// double range = max-min;
|
||||||
@ -468,12 +446,4 @@ public class FloydSteinbergDither {
|
|||||||
// }
|
// }
|
||||||
// return dest;
|
// return dest;
|
||||||
// }
|
// }
|
||||||
private static WritableImage getScaledImage(Image img, int width, int height) {
|
|
||||||
Canvas c = new Canvas(width, height);
|
|
||||||
c.getGraphicsContext2D().drawImage(img, 0, 0, width, height);
|
|
||||||
WritableImage newImg = new WritableImage(width, height);
|
|
||||||
SnapshotParameters sp = new SnapshotParameters();
|
|
||||||
c.snapshot(sp, newImg);
|
|
||||||
return newImg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package org.badvision.outlaweditor.ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author blurry
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ImageConversionPostAction {
|
||||||
|
public void storeConvertedImage(byte[] image);
|
||||||
|
}
|
@ -12,7 +12,7 @@ import java.util.logging.Level;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.event.EventHandler;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
@ -22,6 +22,7 @@ import javafx.scene.control.MenuBar;
|
|||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.WritableImage;
|
import javafx.scene.image.WritableImage;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.HBoxBuilder;
|
import javafx.scene.layout.HBoxBuilder;
|
||||||
import javafx.scene.layout.VBoxBuilder;
|
import javafx.scene.layout.VBoxBuilder;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
@ -31,9 +32,11 @@ import javax.xml.bind.JAXB;
|
|||||||
import org.badvision.outlaweditor.Application;
|
import org.badvision.outlaweditor.Application;
|
||||||
import org.badvision.outlaweditor.FileUtils;
|
import org.badvision.outlaweditor.FileUtils;
|
||||||
import org.badvision.outlaweditor.MythosEditor;
|
import org.badvision.outlaweditor.MythosEditor;
|
||||||
|
import org.badvision.outlaweditor.apple.FloydSteinbergDither;
|
||||||
import org.badvision.outlaweditor.data.TilesetUtils;
|
import org.badvision.outlaweditor.data.TilesetUtils;
|
||||||
import org.badvision.outlaweditor.data.xml.GameData;
|
import org.badvision.outlaweditor.data.xml.GameData;
|
||||||
import org.badvision.outlaweditor.data.xml.Script;
|
import org.badvision.outlaweditor.data.xml.Script;
|
||||||
|
import org.badvision.outlaweditor.ui.impl.ImageConversionWizardController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -120,15 +123,12 @@ public class UIAction {
|
|||||||
currentMenu = new Menu(action.name().replace("_", ""));
|
currentMenu = new Menu(action.name().replace("_", ""));
|
||||||
} else {
|
} else {
|
||||||
MenuItem item = new MenuItem(action.name().replaceAll("_", " "));
|
MenuItem item = new MenuItem(action.name().replaceAll("_", " "));
|
||||||
item.setOnAction(new EventHandler<ActionEvent>() {
|
item.setOnAction((ActionEvent t) -> {
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent t) {
|
|
||||||
try {
|
try {
|
||||||
actionPerformed(action);
|
actionPerformed(action);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
|
Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
currentMenu.getItems().add(item);
|
currentMenu.getItems().add(item);
|
||||||
}
|
}
|
||||||
@ -139,12 +139,7 @@ public class UIAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void quit() {
|
public static void quit() {
|
||||||
confirm("Quit? Are you sure?", new Runnable() {
|
confirm("Quit? Are you sure?", Platform::exit, null);
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Platform.exit();
|
|
||||||
}
|
|
||||||
}, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Image badImage;
|
static Image badImage;
|
||||||
@ -181,14 +176,11 @@ public class UIAction {
|
|||||||
List<Button> buttons = new ArrayList<>();
|
List<Button> buttons = new ArrayList<>();
|
||||||
for (final Choice c : choices) {
|
for (final Choice c : choices) {
|
||||||
Button b = new Button(c.text);
|
Button b = new Button(c.text);
|
||||||
b.setOnAction(new EventHandler<ActionEvent>() {
|
b.setOnAction((ActionEvent t) -> {
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent t) {
|
|
||||||
if (c.handler != null) {
|
if (c.handler != null) {
|
||||||
c.handler.run();
|
c.handler.run();
|
||||||
}
|
}
|
||||||
dialogStage.close();
|
dialogStage.close();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
buttons.add(b);
|
buttons.add(b);
|
||||||
}
|
}
|
||||||
@ -216,4 +208,24 @@ public class UIAction {
|
|||||||
editor.show();
|
editor.show();
|
||||||
return script;
|
return script;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ImageConversionWizardController openImageConversionModal(Image image, FloydSteinbergDither ditherEngine, int targetWidth, int targetHeight, ImageConversionPostAction postAction) {
|
||||||
|
FXMLLoader fxmlLoader = new FXMLLoader(UIAction.class.getResource("/fxml/ImageConversionWizard.fxml"));
|
||||||
|
try {
|
||||||
|
Stage primaryStage = new Stage();
|
||||||
|
AnchorPane node = (AnchorPane) fxmlLoader.load();
|
||||||
|
ImageConversionWizardController controller = fxmlLoader.getController();
|
||||||
|
controller.setDitherEngine(ditherEngine);
|
||||||
|
controller.setOutputDimensions(targetWidth, targetHeight);
|
||||||
|
controller.setPostAction(postAction);
|
||||||
|
controller.setSourceImage(image);
|
||||||
|
Scene s = new Scene(node);
|
||||||
|
primaryStage.setScene(s);
|
||||||
|
primaryStage.show();
|
||||||
|
controller.setStage(primaryStage);
|
||||||
|
return controller;
|
||||||
|
} catch (IOException exception) {
|
||||||
|
throw new RuntimeException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,349 @@
|
|||||||
|
package org.badvision.outlaweditor.ui.impl;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.Slider;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.image.WritableImage;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.badvision.outlaweditor.Application;
|
||||||
|
import org.badvision.outlaweditor.apple.FloydSteinbergDither;
|
||||||
|
import org.badvision.outlaweditor.ui.ImageConversionPostAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FXML Controller class
|
||||||
|
*
|
||||||
|
* @author blurry
|
||||||
|
*/
|
||||||
|
public class ImageConversionWizardController implements Initializable {
|
||||||
|
@FXML
|
||||||
|
private TextField brightnessValue;
|
||||||
|
@FXML
|
||||||
|
private Slider brightnessSlider;
|
||||||
|
@FXML
|
||||||
|
private TextField contrastValue;
|
||||||
|
@FXML
|
||||||
|
private Slider contrastSlider;
|
||||||
|
@FXML
|
||||||
|
private TextField hueValue;
|
||||||
|
@FXML
|
||||||
|
private Slider hueSlider;
|
||||||
|
@FXML
|
||||||
|
private TextField saturationValue;
|
||||||
|
@FXML
|
||||||
|
private Slider saturationSlider;
|
||||||
|
@FXML
|
||||||
|
private TextField outputWidthValue;
|
||||||
|
@FXML
|
||||||
|
private TextField outputHeightValue;
|
||||||
|
@FXML
|
||||||
|
private TextField cropTopValue;
|
||||||
|
@FXML
|
||||||
|
private TextField cropLeftValue;
|
||||||
|
@FXML
|
||||||
|
private TextField cropBottomValue;
|
||||||
|
@FXML
|
||||||
|
private TextField cropRightValue;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue30;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue40;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue01;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue11;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue21;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue31;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue41;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue02;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue12;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue22;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue32;
|
||||||
|
@FXML
|
||||||
|
private TextField coefficientValue42;
|
||||||
|
private final int[][] diffusionCoeffficients = new int[5][3];
|
||||||
|
@FXML
|
||||||
|
private TextField divisorValue;
|
||||||
|
@FXML
|
||||||
|
private ImageView sourceImageView;
|
||||||
|
@FXML
|
||||||
|
private ImageView convertedImageView;
|
||||||
|
/**
|
||||||
|
* Initializes the controller class.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initialize(URL url, ResourceBundle rb) {
|
||||||
|
for (TextField field : new TextField[]{
|
||||||
|
brightnessValue, contrastValue, hueValue, saturationValue,
|
||||||
|
cropBottomValue, cropLeftValue, cropRightValue, cropTopValue,
|
||||||
|
coefficientValue01, coefficientValue02, coefficientValue11, coefficientValue12,
|
||||||
|
coefficientValue21, coefficientValue22, coefficientValue30, coefficientValue31,
|
||||||
|
coefficientValue32, coefficientValue40, coefficientValue41, coefficientValue41,
|
||||||
|
coefficientValue42, divisorValue, outputHeightValue, outputWidthValue
|
||||||
|
}) {
|
||||||
|
configureNumberValidation(field, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
brightnessValue.textProperty().bindBidirectional(brightnessSlider.valueProperty(), NumberFormat.getNumberInstance());
|
||||||
|
contrastValue.textProperty().bindBidirectional(contrastSlider.valueProperty(), NumberFormat.getNumberInstance());
|
||||||
|
hueValue.textProperty().bindBidirectional(hueSlider.valueProperty(), NumberFormat.getNumberInstance());
|
||||||
|
saturationValue.textProperty().bindBidirectional(saturationSlider.valueProperty(), NumberFormat.getNumberInstance());
|
||||||
|
configureFastFloydSteinbergPreset(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stage stage;
|
||||||
|
private ImageConversionPostAction postAction;
|
||||||
|
private Image sourceImage;
|
||||||
|
private WritableImage preprocessedImage;
|
||||||
|
private WritableImage outputPreviewImage;
|
||||||
|
private FloydSteinbergDither ditherEngine;
|
||||||
|
|
||||||
|
public void setStage(Stage stage) {
|
||||||
|
this.stage = stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostAction(ImageConversionPostAction postAction) {
|
||||||
|
this.postAction = postAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDitherEngine(FloydSteinbergDither engine) {
|
||||||
|
this.ditherEngine = engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceImage(Image image) {
|
||||||
|
sourceImage = image;
|
||||||
|
preprocessImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preprocessImage() {
|
||||||
|
preprocessedImage = new WritableImage(sourceImage.getPixelReader(), (int) sourceImage.getWidth(), (int) sourceImage.getHeight());
|
||||||
|
ditherEngine.setSourceImage(preprocessedImage);
|
||||||
|
updateSourceView(preprocessedImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDimensions(int targetWidth, int targetHeight) {
|
||||||
|
ditherEngine.setOutputDimensions(targetWidth, targetHeight);
|
||||||
|
outputWidthValue.setText(String.valueOf(targetWidth));
|
||||||
|
outputHeightValue.setText(String.valueOf(targetHeight));
|
||||||
|
outputPreviewImage = ditherEngine.getPreviewImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOutputWidth() {
|
||||||
|
return Integer.parseInt(outputWidthValue.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOutputHeight() {
|
||||||
|
return Integer.parseInt(outputHeightValue.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSourceView(WritableImage image) {
|
||||||
|
sourceImageView.setImage(image);
|
||||||
|
sourceImageView.setFitWidth(0);
|
||||||
|
sourceImageView.setFitHeight(0);
|
||||||
|
int width = (int) image.getWidth();
|
||||||
|
int height = (int) image.getHeight();
|
||||||
|
defaultTextFieldValues.put(cropRightValue, String.valueOf(width));
|
||||||
|
defaultTextFieldValues.put(cropBottomValue, String.valueOf(height));
|
||||||
|
cropRightValue.setText(String.valueOf(width));
|
||||||
|
cropBottomValue.setText(String.valueOf(height));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void performQuantizePass(ActionEvent event) {
|
||||||
|
ditherEngine.setCoefficients(getCoefficients());
|
||||||
|
ditherEngine.setDivisor(getDivisor());
|
||||||
|
byte[] out = ditherEngine.dither(false);
|
||||||
|
updateConvertedImageWithData(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void performDiffusionPass(ActionEvent event) {
|
||||||
|
ditherEngine.setCoefficients(getCoefficients());
|
||||||
|
ditherEngine.setDivisor(getDivisor());
|
||||||
|
byte[] out = ditherEngine.dither(true);
|
||||||
|
updateConvertedImageWithData(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] lastOutput;
|
||||||
|
private void updateConvertedImageWithData(byte[] data) {
|
||||||
|
lastOutput = data;
|
||||||
|
convertedImageView.setImage(Application.currentPlatform.imageRenderer.renderImage(outputPreviewImage, data, getOutputWidth(), getOutputHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void performOK(ActionEvent event) {
|
||||||
|
postAction.storeConvertedImage(lastOutput);
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void performCancel(ActionEvent event) {
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<TextField, String> defaultTextFieldValues = new HashMap<>();
|
||||||
|
private void configureNumberValidation(TextField field, String defaultValue) {
|
||||||
|
defaultTextFieldValues.put(field, defaultValue);
|
||||||
|
field.textProperty().addListener((ChangeListener) (ObservableValue observable, Object oldValue, Object newValue) -> {
|
||||||
|
if (newValue == null || "".equals(newValue)) {
|
||||||
|
field.textProperty().setValue(defaultTextFieldValues.get(field));
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Double.parseDouble(newValue.toString());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
field.textProperty().setValue(oldValue.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCoefficients(int... coeff) {
|
||||||
|
coefficientValue30.setText(String.valueOf(coeff[3]));
|
||||||
|
coefficientValue40.setText(String.valueOf(coeff[4]));
|
||||||
|
coefficientValue01.setText(String.valueOf(coeff[5]));
|
||||||
|
coefficientValue11.setText(String.valueOf(coeff[6]));
|
||||||
|
coefficientValue21.setText(String.valueOf(coeff[7]));
|
||||||
|
coefficientValue31.setText(String.valueOf(coeff[8]));
|
||||||
|
coefficientValue41.setText(String.valueOf(coeff[9]));
|
||||||
|
coefficientValue02.setText(String.valueOf(coeff[10]));
|
||||||
|
coefficientValue12.setText(String.valueOf(coeff[11]));
|
||||||
|
coefficientValue22.setText(String.valueOf(coeff[12]));
|
||||||
|
coefficientValue32.setText(String.valueOf(coeff[13]));
|
||||||
|
coefficientValue42.setText(String.valueOf(coeff[14]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[][] getCoefficients() {
|
||||||
|
diffusionCoeffficients[0][0] = 0;
|
||||||
|
diffusionCoeffficients[1][0] = 0;
|
||||||
|
diffusionCoeffficients[2][0] = 0;
|
||||||
|
diffusionCoeffficients[3][0] = Integer.parseInt(coefficientValue30.getText());
|
||||||
|
diffusionCoeffficients[4][0] = Integer.parseInt(coefficientValue40.getText());
|
||||||
|
diffusionCoeffficients[0][1] = Integer.parseInt(coefficientValue01.getText());
|
||||||
|
diffusionCoeffficients[1][1] = Integer.parseInt(coefficientValue11.getText());
|
||||||
|
diffusionCoeffficients[2][1] = Integer.parseInt(coefficientValue21.getText());
|
||||||
|
diffusionCoeffficients[3][1] = Integer.parseInt(coefficientValue31.getText());
|
||||||
|
diffusionCoeffficients[4][1] = Integer.parseInt(coefficientValue41.getText());
|
||||||
|
diffusionCoeffficients[0][2] = Integer.parseInt(coefficientValue02.getText());
|
||||||
|
diffusionCoeffficients[1][2] = Integer.parseInt(coefficientValue12.getText());
|
||||||
|
diffusionCoeffficients[2][2] = Integer.parseInt(coefficientValue22.getText());
|
||||||
|
diffusionCoeffficients[3][2] = Integer.parseInt(coefficientValue32.getText());
|
||||||
|
diffusionCoeffficients[4][2] = Integer.parseInt(coefficientValue42.getText());
|
||||||
|
return diffusionCoeffficients;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDivisor(int div) {
|
||||||
|
divisorValue.setText(String.valueOf(div));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getDivisor() {
|
||||||
|
return Integer.valueOf(divisorValue.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/
|
||||||
|
@FXML
|
||||||
|
private void configureFloydSteinbergPreset(ActionEvent event) {
|
||||||
|
setCoefficients(
|
||||||
|
0, 0, 0, 7, 0,
|
||||||
|
0, 3, 5, 1, 0,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
);
|
||||||
|
setDivisor(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void configureFastFloydSteinbergPreset(ActionEvent event) {
|
||||||
|
setCoefficients(
|
||||||
|
0, 0, 0, 3, 0,
|
||||||
|
0, 0, 3, 2, 0,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
);
|
||||||
|
setDivisor(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void configureJarvisJudiceNinkePreset(ActionEvent event) {
|
||||||
|
setCoefficients(
|
||||||
|
0, 0, 0, 7, 5,
|
||||||
|
3, 5, 7, 5, 3,
|
||||||
|
1, 3, 5, 3, 1
|
||||||
|
);
|
||||||
|
setDivisor(48);
|
||||||
|
}
|
||||||
|
@FXML
|
||||||
|
private void configureStuckiPreset(ActionEvent event) {
|
||||||
|
setCoefficients(
|
||||||
|
0, 0, 0, 8, 4,
|
||||||
|
2, 4, 8, 4, 2,
|
||||||
|
1, 2, 4, 2, 1
|
||||||
|
);
|
||||||
|
setDivisor(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void configureAtkinsonPreset(ActionEvent event) {
|
||||||
|
setCoefficients(
|
||||||
|
0, 0, 0, 1, 1,
|
||||||
|
0, 1, 1, 1, 0,
|
||||||
|
0, 0, 1, 0, 0
|
||||||
|
);
|
||||||
|
setDivisor(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void configureBurkesPreset(ActionEvent event) {
|
||||||
|
setCoefficients(
|
||||||
|
0, 0, 0, 8, 4,
|
||||||
|
2, 4, 8, 4, 2,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
);
|
||||||
|
setDivisor(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void configureSierraPreset(ActionEvent event) {
|
||||||
|
setCoefficients(
|
||||||
|
0, 0, 0, 5, 3,
|
||||||
|
2, 4, 5, 4, 2,
|
||||||
|
0, 2, 3, 2, 0
|
||||||
|
);
|
||||||
|
setDivisor(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void configureTwoRowSierraPreset(ActionEvent event) {
|
||||||
|
setCoefficients(
|
||||||
|
0, 0, 0, 4, 3,
|
||||||
|
1, 2, 3, 2, 1,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
);
|
||||||
|
setDivisor(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void configureSierraLitePreset(ActionEvent event) {
|
||||||
|
setCoefficients(
|
||||||
|
0, 0, 0, 2, 0,
|
||||||
|
0, 1, 1, 0, 0,
|
||||||
|
0, 0, 0, 0, 0
|
||||||
|
);
|
||||||
|
setDivisor(4);
|
||||||
|
}
|
||||||
|
}
|
219
OutlawEditor/src/main/resources/fxml/imageConversionWizard.fxml
Normal file
219
OutlawEditor/src/main/resources/fxml/imageConversionWizard.fxml
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.image.*?>
|
||||||
|
<?import javafx.scene.shape.*?>
|
||||||
|
<?import javafx.geometry.*?>
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.net.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<AnchorPane id="AnchorPane" maxHeight="482.0" maxWidth="600.0" prefHeight="482.0" prefWidth="600.0" styleClass="mainFxmlClass" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.badvision.outlaweditor.ui.impl.ImageConversionWizardController">
|
||||||
|
<stylesheets>
|
||||||
|
<URL value="@/styles/imageconversionwizard.css" />
|
||||||
|
</stylesheets>
|
||||||
|
<children>
|
||||||
|
<HBox layoutX="14.0" layoutY="14.0" styleClass="imageViews" AnchorPane.bottomAnchor="276.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0">
|
||||||
|
<children>
|
||||||
|
<StackPane HBox.hgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<ScrollPane fitToWidth="true" minHeight="192.0" pannable="true" styleClass="imageViewScroll">
|
||||||
|
<content>
|
||||||
|
<AnchorPane minHeight="188.0" styleClass="imageView">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="sourceImageView" fitHeight="185.0" fitWidth="271.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
<Label alignment="BOTTOM_CENTER" text="Source Image" textAlignment="CENTER" StackPane.alignment="BOTTOM_CENTER">
|
||||||
|
<StackPane.margin>
|
||||||
|
<Insets bottom="-20.0" />
|
||||||
|
</StackPane.margin>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
</StackPane>
|
||||||
|
<Region prefHeight="1" prefWidth="14.0" />
|
||||||
|
<StackPane HBox.hgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<ScrollPane fitToWidth="true" minHeight="192.0" pannable="true" styleClass="imageViewScroll">
|
||||||
|
<content>
|
||||||
|
<AnchorPane minHeight="188.0" styleClass="imageView">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="convertedImageView" fitHeight="185.0" fitWidth="271.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
<Label alignment="BOTTOM_CENTER" text="Converted" textAlignment="CENTER" StackPane.alignment="BOTTOM_CENTER">
|
||||||
|
<StackPane.margin>
|
||||||
|
<Insets bottom="-20.0" />
|
||||||
|
</StackPane.margin>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
</StackPane>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<Separator layoutX="14.0" layoutY="227.0" prefHeight="9.0" prefWidth="442.0" AnchorPane.bottomAnchor="246.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" />
|
||||||
|
<TabPane layoutX="14.0" layoutY="231.0" prefHeight="203.0" prefWidth="582.0" side="BOTTOM" tabClosingPolicy="UNAVAILABLE" AnchorPane.bottomAnchor="48.0" AnchorPane.leftAnchor="9.0" AnchorPane.rightAnchor="9.0">
|
||||||
|
<tabs>
|
||||||
|
<Tab text="Source Adjustments">
|
||||||
|
<content>
|
||||||
|
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<Label layoutX="14.0" layoutY="14.0" text="Brightness" AnchorPane.leftAnchor="9.0" />
|
||||||
|
<TextField fx:id="brightnessValue" layoutX="98.0" layoutY="9.0" prefHeight="26.0" prefWidth="60.0" />
|
||||||
|
<Slider fx:id="brightnessSlider" blockIncrement="0.1" layoutX="148.0" layoutY="15.0" majorTickUnit="0.5" max="1.0" min="-1.0" minorTickCount="10" prefHeight="16.0" prefWidth="415.0" showTickMarks="true" snapToTicks="true" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="0.0" />
|
||||||
|
|
||||||
|
<Label layoutX="14.0" layoutY="43.0" text="Contrast" AnchorPane.leftAnchor="9.0" />
|
||||||
|
<TextField fx:id="contrastValue" layoutX="98.0" layoutY="38.0" prefHeight="26.0" prefWidth="60.0" />
|
||||||
|
<Slider fx:id="contrastSlider" blockIncrement="0.1" layoutX="148.0" layoutY="44.0" majorTickUnit="0.5" max="1.0" min="-1.0" minorTickCount="10" prefHeight="16.0" prefWidth="415.0" showTickMarks="true" snapToTicks="true" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="0.0" />
|
||||||
|
|
||||||
|
<Label layoutX="14.0" layoutY="72.0" text="Hue" AnchorPane.leftAnchor="9.0" />
|
||||||
|
<TextField fx:id="hueValue" layoutX="98.0" layoutY="67.0" prefHeight="26.0" prefWidth="60.0" />
|
||||||
|
<Slider fx:id="hueSlider" blockIncrement="0.1" layoutX="148.0" layoutY="73.0" majorTickUnit="0.5" max="1.0" min="-1.0" minorTickCount="10" prefHeight="16.0" prefWidth="415.0" showTickMarks="true" snapToTicks="true" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="0.0" />
|
||||||
|
|
||||||
|
<Label layoutX="14.0" layoutY="101.0" text="Saturation" AnchorPane.leftAnchor="9.0" />
|
||||||
|
<TextField fx:id="saturationValue" layoutX="98.0" layoutY="96.0" prefHeight="26.0" prefWidth="60.0" />
|
||||||
|
<Slider fx:id="saturationSlider" blockIncrement="0.1" layoutX="148.0" layoutY="102.0" majorTickUnit="0.5" max="1.0" min="-1.0" minorTickCount="10" prefHeight="16.0" prefWidth="415.0" showTickMarks="true" snapToTicks="true" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="0.0" />
|
||||||
|
|
||||||
|
<Label layoutX="9.0" layoutY="130.0" text="Crop Top (px)" />
|
||||||
|
<TextField fx:id="cropTopValue" layoutX="98.0" layoutY="125.0" prefHeight="26.0" prefWidth="60.0" text="0" />
|
||||||
|
|
||||||
|
<Label layoutX="168.0" layoutY="130.0" text="Left" />
|
||||||
|
<TextField fx:id="cropLeftValue" layoutX="196.0" layoutY="125.0" prefHeight="26.0" prefWidth="60.0" text="0" />
|
||||||
|
|
||||||
|
<Label layoutX="266.0" layoutY="130.0" text="Bottom" />
|
||||||
|
<TextField fx:id="cropBottomValue" layoutX="315.0" layoutY="125.0" prefHeight="26.0" prefWidth="60.0" />
|
||||||
|
|
||||||
|
<Label layoutX="385.0" layoutY="130.0" text="Right" />
|
||||||
|
<TextField fx:id="cropRightValue" layoutX="422.0" layoutY="125.0" prefHeight="26.0" prefWidth="60.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</Tab>
|
||||||
|
<Tab text="Diffusion Coefficients">
|
||||||
|
<content>
|
||||||
|
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="175.0" prefWidth="572.0">
|
||||||
|
<children>
|
||||||
|
<GridPane layoutX="14.0" layoutY="14.0" prefHeight="110.0" prefWidth="564.0" AnchorPane.leftAnchor="4.0" AnchorPane.rightAnchor="4.0" AnchorPane.topAnchor="9.0">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label alignment="CENTER" contentDisplay="CENTER" prefHeight="16.0" prefWidth="17.0" text="X" textAlignment="CENTER" GridPane.columnIndex="2" GridPane.halignment="CENTER" />
|
||||||
|
<TextField fx:id="coefficientValue30" text="0" GridPane.columnIndex="3">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue40" text="0" GridPane.columnIndex="4">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue01" text="0" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue11" text="0" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue21" text="0" GridPane.columnIndex="2" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue31" text="0" GridPane.columnIndex="3" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue41" text="0" GridPane.columnIndex="4" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue02" text="0" GridPane.rowIndex="2">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue12" text="0" GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue22" text="0" GridPane.columnIndex="2" GridPane.rowIndex="2">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue32" text="0" GridPane.columnIndex="3" GridPane.rowIndex="2">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<TextField fx:id="coefficientValue42" text="0" GridPane.columnIndex="4" GridPane.rowIndex="2">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<Separator prefWidth="200.0" />
|
||||||
|
<Separator prefWidth="200.0" GridPane.columnIndex="1" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
<SplitMenuButton layoutX="430.0" layoutY="120.0" mnemonicParsing="false" text="Select preset..." AnchorPane.bottomAnchor="13.0" AnchorPane.rightAnchor="9.0">
|
||||||
|
<items>
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#configureFloydSteinbergPreset" text="Floyd-Steinberg" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#configureFastFloydSteinbergPreset" text="Fast Floyd-Steinberg" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#configureJarvisJudiceNinkePreset" text="Jarvis-Judice-Ninke" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#configureStuckiPreset" text="Stucki" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#configureAtkinsonPreset" text="Atkinson" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#configureBurkesPreset" text="Burkes" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#configureSierraPreset" text="Sierra" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#configureTwoRowSierraPreset" text="Two-Row Sierra" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#configureSierraLitePreset" text="Sierra Lite" />
|
||||||
|
</items>
|
||||||
|
</SplitMenuButton>
|
||||||
|
<TextField fx:id="divisorValue" layoutX="375.0" layoutY="131.0" prefHeight="26.0" prefWidth="46.0" text="1" AnchorPane.rightAnchor="161.0" />
|
||||||
|
<Label layoutX="326.0" layoutY="136.0" text="Divisor" AnchorPane.rightAnchor="212.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</Tab>
|
||||||
|
<Tab text="Output Settings">
|
||||||
|
<content>
|
||||||
|
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<Label alignment="CENTER_RIGHT" layoutX="14.0" layoutY="13.0" prefHeight="16.0" prefWidth="53.0" text="Width" textAlignment="RIGHT" />
|
||||||
|
<TextField fx:id="outputWidthValue" layoutX="69.0" layoutY="8.0" prefHeight="16.0" prefWidth="122.0" promptText="Width (in pixels)" />
|
||||||
|
|
||||||
|
<Label alignment="CENTER_RIGHT" layoutX="14.0" layoutY="44.0" prefHeight="16.0" prefWidth="53.0" text="Height" textAlignment="RIGHT" />
|
||||||
|
<TextField fx:id="outputHeightValue" layoutX="69.0" layoutY="39.0" prefHeight="16.0" prefWidth="122.0" promptText="Height (in pixels)" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</Tab>
|
||||||
|
</tabs>
|
||||||
|
</TabPane>
|
||||||
|
<Button onAction="#performQuantizePass" layoutX="14.0" layoutY="412.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="141.0" text="Quantize Pass" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" />
|
||||||
|
<Button onAction="#performDiffusionPass" layoutX="159.0" layoutY="412.0" mnemonicParsing="false" text="Error Diffusion Pass" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="162.0" />
|
||||||
|
<Button onAction="#performOK" layoutX="482.0" layoutY="412.0" mnemonicParsing="false" text="OK" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="81.0" />
|
||||||
|
<Button onAction="#performCancel" layoutX="526.0" layoutY="412.0" mnemonicParsing="false" text="Cancel" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="14.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
@ -0,0 +1,3 @@
|
|||||||
|
.mainFxmlClass {
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user