Added image adjustments and tweaked HGR conversion some more (and fixed another bug...)

This commit is contained in:
Brendan Robert 2014-10-02 01:25:54 -05:00
parent 489a05803e
commit b11da29c56
4 changed files with 97 additions and 95 deletions

View File

@ -47,8 +47,8 @@ import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.hgrToDhgr;
public class ImageDitherEngine { public class ImageDitherEngine {
int byteRenderWidth; int byteRenderWidth;
final int errorWindow = 6; int errorWindow = 7;
final int overlap = 2; int overlap = 3;
WritableImage source; WritableImage source;
byte[] screen; byte[] screen;
Platform platform; Platform platform;
@ -56,7 +56,6 @@ public class ImageDitherEngine {
int height; int height;
int divisor; int divisor;
int[][] coefficients; int[][] coefficients;
boolean resetOutput = true;
public ImageDitherEngine(Platform platform) { public ImageDitherEngine(Platform platform) {
this.platform = platform; this.platform = platform;
@ -65,7 +64,6 @@ public class ImageDitherEngine {
public void setSourceImage(Image img) { public void setSourceImage(Image img) {
source = getScaledImage(img, bufferWidth * byteRenderWidth, height); source = getScaledImage(img, bufferWidth * byteRenderWidth, height);
resetOutput = true;
} }
private static WritableImage getScaledImage(Image img, int width, int height) { private static WritableImage getScaledImage(Image img, int width, int height) {
@ -85,7 +83,6 @@ public class ImageDitherEngine {
this.bufferWidth = width; this.bufferWidth = width;
this.height = height; this.height = height;
screen = platform.imageRenderer.createImageBuffer(width, height); screen = platform.imageRenderer.createImageBuffer(width, height);
resetOutput = true;
} }
public void setDivisor(int divisor) { public void setDivisor(int divisor) {
@ -109,31 +106,20 @@ public class ImageDitherEngine {
WritableImage tmpScaled2; WritableImage tmpScaled2;
int[] scanline; int[] scanline;
List<Integer> pixels; List<Integer> pixels;
public byte[] restartDither(int value) {
keepScaled = new WritableImage(source.getPixelReader(), 560, 192);
tmpScaled1 = new WritableImage(source.getPixelReader(), 560, 192);
tmpScaled2 = new WritableImage(source.getPixelReader(), 560, 192);
for (int i = 0; i < screen.length; i++) {
screen[i] = (byte) (value >= 0 ? value : (int) Math.floor(Math.random() * 256.0));
}
scanline = new int[3];
pixels = new ArrayList<>();
return screen;
}
public Image getScratchBuffer() { public Image getScratchBuffer() {
return keepScaled; return keepScaled;
} }
public byte[] dither(boolean propagateError) { public byte[] dither(boolean propagateError) {
if (resetOutput) { keepScaled = new WritableImage(source.getPixelReader(), 560, 192);
restartDither(0); tmpScaled1 = new WritableImage(source.getPixelReader(), 560, 192);
resetOutput = false; tmpScaled2 = new WritableImage(source.getPixelReader(), 560, 192);
for (int i = 0; i < screen.length; i++) {
screen[i] = (byte) 0;
} }
keepScaled.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0); scanline = new int[3];
tmpScaled1.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0); pixels = new ArrayList<>();
tmpScaled2.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0);
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < bufferWidth; x += 2) { for (int x = 0; x < bufferWidth; x += 2) {
switch (platform) { switch (platform) {
@ -161,11 +147,11 @@ public class ImageDitherEngine {
next = screen[(y + startY) * bufferWidth + startX + x + 2] & 255; next = screen[(y + startY) * bufferWidth + startX + x + 2] & 255;
} }
// 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; long leastError = Long.MAX_VALUE;
for (int hi = 0; hi < 2; hi++) { for (int hi = 0; hi < 2; hi++) {
tmpScaled2.getPixelWriter().setPixels(0, y, 560, (y < 190) ? 3 : (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y); tmpScaled2.getPixelWriter().setPixels(0, y, 560, (y < 190) ? 3 : (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
int b1 = (hi << 7); int b1 = (hi << 7);
int totalError = 0; long totalError = 0;
for (int c = 0; c < 7; c++) { for (int c = 0; c < 7; c++) {
// for (int c = 6; c >= 0; c--) { // for (int c = 6; c >= 0; c--) {
int on = b1 | (1 << c); int on = b1 | (1 << c);
@ -176,7 +162,7 @@ public class ImageDitherEngine {
i = hgrToDhgr[(i & 0x010000000) >> 20 | off][bb2]; i = hgrToDhgr[(i & 0x010000000) >> 20 | off][bb2];
scanline[1] = i; scanline[1] = i;
// scanline[2] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0] & 0x0fffffff; // scanline[2] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0] & 0x0fffffff;
int errorOff = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline); long errorOff = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline);
int off1 = pixels.get(c * 2 + 28); int off1 = pixels.get(c * 2 + 28);
int off2 = pixels.get(c * 2 + 29); int off2 = pixels.get(c * 2 + 29);
// get values for "on" // get values for "on"
@ -185,7 +171,7 @@ public class ImageDitherEngine {
i = hgrToDhgr[(i & 0x010000000) >> 20 | on][bb2]; i = hgrToDhgr[(i & 0x010000000) >> 20 | on][bb2];
scanline[1] = i; scanline[1] = i;
// scanline[2] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0] & 0x0fffffff; // scanline[2] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0] & 0x0fffffff;
int errorOn = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline); long errorOn = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline);
int on1 = pixels.get(c * 2 + 28); int on1 = pixels.get(c * 2 + 28);
int on2 = pixels.get(c * 2 + 29); int on2 = pixels.get(c * 2 + 29);
int[] col1; int[] col1;
@ -203,7 +189,7 @@ public class ImageDitherEngine {
} }
if (propagateError) { if (propagateError) {
propagateError(x * 14 + c * 2, y, tmpScaled2, col1); propagateError(x * 14 + c * 2, y, tmpScaled2, col1);
propagateError(x * 14 + c * 2, y, tmpScaled2, col2); propagateError(x * 14 + c * 2 + 1, y, tmpScaled2, col2);
} }
} }
if (totalError < leastError) { if (totalError < leastError) {
@ -214,11 +200,11 @@ public class ImageDitherEngine {
} }
keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 190) ? 3 : (y < 191) ? 2 : 1, tmpScaled1.getPixelReader(), 0, y); keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 190) ? 3 : (y < 191) ? 2 : 1, tmpScaled1.getPixelReader(), 0, y);
// Second byte, compared with a sliding window encompassing the next byte, if any. // Second byte, compared with a sliding window encompassing the next byte, if any.
leastError = Integer.MAX_VALUE; leastError = Long.MAX_VALUE;
for (int hi = 0; hi < 2; hi++) { for (int hi = 0; hi < 2; hi++) {
tmpScaled2.getPixelWriter().setPixels(0, y, 560, (y < 190) ? 3 : (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y); tmpScaled2.getPixelWriter().setPixels(0, y, 560, (y < 190) ? 3 : (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
int b2 = (hi << 7); int b2 = (hi << 7);
int totalError = 0; long totalError = 0;
for (int c = 0; c < 7; c++) { for (int c = 0; c < 7; c++) {
// for (int c = 6; c >= 0; c--) { // for (int c = 6; c >= 0; c--) {
int on = b2 | (1 << c); int on = b2 | (1 << c);
@ -228,14 +214,14 @@ public class ImageDitherEngine {
scanline[0] = i; scanline[0] = i;
scanline[1] = hgrToDhgr[(i & 0x010000000) >> 20 | next][0]; scanline[1] = hgrToDhgr[(i & 0x010000000) >> 20 | next][0];
// int errorOff = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline); // int errorOff = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline);
int errorOff = getError(x * 14 + 14 - overlap + c * 2, y, 14 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline); long errorOff = getError(x * 14 + 14 - overlap + c * 2, y, 14 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline);
int off1 = pixels.get(c * 2 + 14); int off1 = pixels.get(c * 2 + 14);
int off2 = pixels.get(c * 2 + 15); int off2 = pixels.get(c * 2 + 15);
// get values for "on" // get values for "on"
i = hgrToDhgr[bb1][on]; i = hgrToDhgr[bb1][on];
scanline[0] = i; scanline[0] = i;
scanline[1] = hgrToDhgr[(i & 0x010000000) >> 20 | next][0]; scanline[1] = hgrToDhgr[(i & 0x010000000) >> 20 | next][0];
int errorOn = getError(x * 14 + 14 - overlap + c * 2, y, 14 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline); long errorOn = getError(x * 14 + 14 - overlap + c * 2, y, 14 + c * 2 - overlap, errorWindow, pixels, tmpScaled2.getPixelReader(), scanline);
int on1 = pixels.get(c * 2 + 14); int on1 = pixels.get(c * 2 + 14);
int on2 = pixels.get(c * 2 + 15); int on2 = pixels.get(c * 2 + 15);
int[] col1; int[] col1;

View File

@ -5,6 +5,9 @@ import java.text.NumberFormat;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
@ -12,8 +15,10 @@ import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.image.PixelReader;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.badvision.outlaweditor.Application; import org.badvision.outlaweditor.Application;
@ -26,6 +31,7 @@ import org.badvision.outlaweditor.ui.ImageConversionPostAction;
* @author blurry * @author blurry
*/ */
public class ImageConversionWizardController implements Initializable { public class ImageConversionWizardController implements Initializable {
@FXML @FXML
private TextField brightnessValue; private TextField brightnessValue;
@FXML @FXML
@ -85,8 +91,9 @@ public class ImageConversionWizardController implements Initializable {
private ImageView sourceImageView; private ImageView sourceImageView;
@FXML @FXML
private ImageView convertedImageView; private ImageView convertedImageView;
@FXML
private TextField fillValue; private ColorAdjust imageAdjustments = new ColorAdjust();
/** /**
* Initializes the controller class. * Initializes the controller class.
*/ */
@ -94,19 +101,29 @@ public class ImageConversionWizardController implements Initializable {
public void initialize(URL url, ResourceBundle rb) { public void initialize(URL url, ResourceBundle rb) {
for (TextField field : new TextField[]{ for (TextField field : new TextField[]{
brightnessValue, contrastValue, hueValue, saturationValue, brightnessValue, contrastValue, hueValue, saturationValue,
cropBottomValue, cropLeftValue, cropRightValue, cropTopValue, cropBottomValue, cropLeftValue, cropRightValue, cropTopValue,
coefficientValue01, coefficientValue02, coefficientValue11, coefficientValue12, coefficientValue01, coefficientValue02, coefficientValue11, coefficientValue12,
coefficientValue21, coefficientValue22, coefficientValue30, coefficientValue31, coefficientValue21, coefficientValue22, coefficientValue30, coefficientValue31,
coefficientValue32, coefficientValue40, coefficientValue41, coefficientValue41, coefficientValue32, coefficientValue40, coefficientValue41, coefficientValue41,
coefficientValue42, divisorValue, outputHeightValue, outputWidthValue, fillValue coefficientValue42, divisorValue, outputHeightValue, outputWidthValue
}) { }) {
configureNumberValidation(field, "0"); configureNumberValidation(field, "0");
} }
brightnessValue.textProperty().bindBidirectional(brightnessSlider.valueProperty(), NumberFormat.getNumberInstance()); brightnessValue.textProperty().bindBidirectional(brightnessSlider.valueProperty(), NumberFormat.getNumberInstance());
contrastValue.textProperty().bindBidirectional(contrastSlider.valueProperty(), NumberFormat.getNumberInstance()); contrastValue.textProperty().bindBidirectional(contrastSlider.valueProperty(), NumberFormat.getNumberInstance());
hueValue.textProperty().bindBidirectional(hueSlider.valueProperty(), NumberFormat.getNumberInstance()); hueValue.textProperty().bindBidirectional(hueSlider.valueProperty(), NumberFormat.getNumberInstance());
saturationValue.textProperty().bindBidirectional(saturationSlider.valueProperty(), NumberFormat.getNumberInstance()); saturationValue.textProperty().bindBidirectional(saturationSlider.valueProperty(), NumberFormat.getNumberInstance());
brightnessValue.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue)
-> javafx.application.Platform.runLater(this::updateImageAdjustments));
contrastValue.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue)
-> javafx.application.Platform.runLater(this::updateImageAdjustments));
hueValue.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue)
-> javafx.application.Platform.runLater(this::updateImageAdjustments));
saturationValue.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue)
-> javafx.application.Platform.runLater(this::updateImageAdjustments));
configureFastFloydSteinbergPreset(null); configureFastFloydSteinbergPreset(null);
} }
@ -128,34 +145,48 @@ public class ImageConversionWizardController implements Initializable {
public void setDitherEngine(ImageDitherEngine engine) { public void setDitherEngine(ImageDitherEngine engine) {
this.ditherEngine = engine; this.ditherEngine = engine;
} }
public void setSourceImage(Image image) { public void setSourceImage(Image image) {
sourceImage = image; sourceImage = image;
preprocessImage(); preprocessImage();
} }
private void updateImageAdjustments() {
double hue = Double.parseDouble(hueValue.getText());
double saturation = Double.parseDouble(saturationValue.getText());
double brightness = Double.parseDouble(brightnessValue.getText());
double contrast = Double.parseDouble(contrastValue.getText());
imageAdjustments = new ColorAdjust();
imageAdjustments.setContrast(contrast);
imageAdjustments.setBrightness(brightness);
imageAdjustments.setHue(hue);
imageAdjustments.setSaturation(saturation);
sourceImageView.setEffect(imageAdjustments);
}
private void preprocessImage() { private void preprocessImage() {
preprocessedImage = new WritableImage(sourceImage.getPixelReader(), (int) sourceImage.getWidth(), (int) sourceImage.getHeight()); PixelReader pixelReader = sourceImage.getPixelReader();
ditherEngine.setSourceImage(preprocessedImage); preprocessedImage = new WritableImage(pixelReader, (int) sourceImage.getWidth(), (int) sourceImage.getHeight());
updateSourceView(preprocessedImage); updateSourceView(preprocessedImage);
} }
public void setOutputDimensions(int targetWidth, int targetHeight) { public void setOutputDimensions(int targetWidth, int targetHeight) {
ditherEngine.setOutputDimensions(targetWidth, targetHeight); ditherEngine.setOutputDimensions(targetWidth, targetHeight);
outputWidthValue.setText(String.valueOf(targetWidth)); outputWidthValue.setText(String.valueOf(targetWidth));
outputHeightValue.setText(String.valueOf(targetHeight)); outputHeightValue.setText(String.valueOf(targetHeight));
outputPreviewImage = ditherEngine.getPreviewImage(); outputPreviewImage = ditherEngine.getPreviewImage();
} }
public int getOutputWidth() { public int getOutputWidth() {
return Integer.parseInt(outputWidthValue.getText()); return Integer.parseInt(outputWidthValue.getText());
} }
public int getOutputHeight() { public int getOutputHeight() {
return Integer.parseInt(outputHeightValue.getText()); return Integer.parseInt(outputHeightValue.getText());
} }
private void updateSourceView(WritableImage image) { private void updateSourceView(Image image) {
sourceImageView.setImage(image); sourceImageView.setImage(image);
sourceImageView.setFitWidth(0); sourceImageView.setFitWidth(0);
sourceImageView.setFitHeight(0); sourceImageView.setFitHeight(0);
@ -165,35 +196,27 @@ public class ImageConversionWizardController implements Initializable {
defaultTextFieldValues.put(cropBottomValue, String.valueOf(height)); defaultTextFieldValues.put(cropBottomValue, String.valueOf(height));
cropRightValue.setText(String.valueOf(width)); cropRightValue.setText(String.valueOf(width));
cropBottomValue.setText(String.valueOf(height)); cropBottomValue.setText(String.valueOf(height));
}
@FXML
private void fillOutput(ActionEvent event) {
int fill = Integer.parseInt(fillValue.getText());
updateConvertedImageWithData(ditherEngine.restartDither(fill));
} }
@FXML
private void randomizeOutput(ActionEvent event) {
updateConvertedImageWithData(ditherEngine.restartDither(-1));
}
@FXML @FXML
private void performQuantizePass(ActionEvent event) { private void performQuantizePass(ActionEvent event) {
ditherEngine.setCoefficients(getCoefficients()); prepareForConversion();
ditherEngine.setDivisor(getDivisor());
byte[] out = ditherEngine.dither(false); byte[] out = ditherEngine.dither(false);
updateConvertedImageWithData(out); updateConvertedImageWithData(out);
} }
@FXML @FXML
private void performDiffusionPass(ActionEvent event) { private void performDiffusionPass(ActionEvent event) {
ditherEngine.setCoefficients(getCoefficients()); prepareForConversion();
ditherEngine.setDivisor(getDivisor());
byte[] out = ditherEngine.dither(true); byte[] out = ditherEngine.dither(true);
sourceImageView.setImage(ditherEngine.getScratchBuffer());
updateConvertedImageWithData(out); updateConvertedImageWithData(out);
} }
private void prepareForConversion() {
ditherEngine.setCoefficients(getCoefficients());
ditherEngine.setDivisor(getDivisor());
ditherEngine.setSourceImage(sourceImageView.snapshot(null, null));
}
byte[] lastOutput; byte[] lastOutput;
private void updateConvertedImageWithData(byte[] data) { private void updateConvertedImageWithData(byte[] data) {
@ -211,13 +234,18 @@ public class ImageConversionWizardController implements Initializable {
private void performCancel(ActionEvent event) { private void performCancel(ActionEvent event) {
stage.close(); stage.close();
} }
private final Map<TextField, String> defaultTextFieldValues = new HashMap<>(); private final Map<TextField, String> defaultTextFieldValues = new HashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
private void configureNumberValidation(TextField field, String defaultValue) { private void configureNumberValidation(TextField field, String defaultValue) {
defaultTextFieldValues.put(field, defaultValue); defaultTextFieldValues.put(field, defaultValue);
field.textProperty().addListener((ChangeListener) (ObservableValue observable, Object oldValue, Object newValue) -> { field.textProperty().addListener((ChangeListener) (ObservableValue observable, Object oldValue, Object newValue) -> {
if (newValue == null || "".equals(newValue)) { if (newValue == null || "".equals(newValue)) {
field.textProperty().setValue(defaultTextFieldValues.get(field)); scheduler.schedule(() -> {
if (null == field.textProperty().getValue() || field.textProperty().getValue().isEmpty()) {
field.textProperty().setValue(defaultTextFieldValues.get(field));
}
}, 250, TimeUnit.MILLISECONDS);
} else { } else {
try { try {
Double.parseDouble(newValue.toString()); Double.parseDouble(newValue.toString());
@ -227,7 +255,7 @@ public class ImageConversionWizardController implements Initializable {
} }
}); });
} }
private void setCoefficients(int... coeff) { private void setCoefficients(int... coeff) {
coefficientValue30.setText(String.valueOf(coeff[3])); coefficientValue30.setText(String.valueOf(coeff[3]));
coefficientValue40.setText(String.valueOf(coeff[4])); coefficientValue40.setText(String.valueOf(coeff[4]));
@ -242,7 +270,7 @@ public class ImageConversionWizardController implements Initializable {
coefficientValue32.setText(String.valueOf(coeff[13])); coefficientValue32.setText(String.valueOf(coeff[13]));
coefficientValue42.setText(String.valueOf(coeff[14])); coefficientValue42.setText(String.valueOf(coeff[14]));
} }
private int[][] getCoefficients() { private int[][] getCoefficients() {
diffusionCoeffficients[0][0] = 0; diffusionCoeffficients[0][0] = 0;
diffusionCoeffficients[1][0] = 0; diffusionCoeffficients[1][0] = 0;
@ -261,11 +289,11 @@ public class ImageConversionWizardController implements Initializable {
diffusionCoeffficients[4][2] = Integer.parseInt(coefficientValue42.getText()); diffusionCoeffficients[4][2] = Integer.parseInt(coefficientValue42.getText());
return diffusionCoeffficients; return diffusionCoeffficients;
} }
private void setDivisor(int div) { private void setDivisor(int div) {
divisorValue.setText(String.valueOf(div)); divisorValue.setText(String.valueOf(div));
} }
private int getDivisor() { private int getDivisor() {
return Integer.valueOf(divisorValue.getText()); return Integer.valueOf(divisorValue.getText());
} }
@ -298,8 +326,9 @@ public class ImageConversionWizardController implements Initializable {
3, 5, 7, 5, 3, 3, 5, 7, 5, 3,
1, 3, 5, 3, 1 1, 3, 5, 3, 1
); );
setDivisor(48); setDivisor(48);
} }
@FXML @FXML
private void configureStuckiPreset(ActionEvent event) { private void configureStuckiPreset(ActionEvent event) {
setCoefficients( setCoefficients(
@ -359,4 +388,5 @@ public class ImageConversionWizardController implements Initializable {
); );
setDivisor(4); setDivisor(4);
} }
} }

View File

@ -4,10 +4,8 @@ import org.badvision.outlaweditor.ui.EntitySelectorCell;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import javafx.scene.control.cell.ComboBoxListCell; import javafx.scene.control.cell.ComboBoxListCell;
import javafx.util.Callback;
import org.badvision.outlaweditor.Application; import org.badvision.outlaweditor.Application;
import org.badvision.outlaweditor.Editor; import org.badvision.outlaweditor.Editor;
import org.badvision.outlaweditor.ImageEditor; import org.badvision.outlaweditor.ImageEditor;
@ -50,14 +48,9 @@ public class ImageEditorTabControllerImpl extends ImageEditorTabController {
} }
} }
}); });
imageSelector.setCellFactory(new Callback<ListView<Image>, ListCell<Image>>() { imageSelector.setCellFactory((ListView<Image> param) -> new EntitySelectorCell<Image>(imageNameField) {
@Override @Override
public ListCell<Image> call(ListView<Image> param) { public void finishUpdate(Image item) {
return new EntitySelectorCell<Image>(imageNameField) {
@Override
public void finishUpdate(Image item) {
}
};
} }
}); });
} }
@ -149,14 +142,11 @@ public class ImageEditorTabControllerImpl extends ImageEditorTabController {
if (currentImage == null) { if (currentImage == null) {
return; return;
} }
confirm("Delete image '" + currentImage.getName() + "'. Are you sure?", new Runnable() { confirm("Delete image '" + currentImage.getName() + "'. Are you sure?", () -> {
@Override Image del = currentImage;
public void run() { setCurrentImage(null);
Image del = currentImage; Application.gameData.getImage().remove(del);
setCurrentImage(null); rebuildImageSelector();
Application.gameData.getImage().remove(del);
rebuildImageSelector();
}
}, null); }, null);
} }

View File

@ -205,18 +205,14 @@
<Label alignment="CENTER_RIGHT" layoutX="14.0" layoutY="44.0" prefHeight="16.0" prefWidth="53.0" text="Height" textAlignment="RIGHT" /> <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)" /> <TextField fx:id="outputHeightValue" layoutX="69.0" layoutY="39.0" prefHeight="16.0" prefWidth="122.0" promptText="Height (in pixels)" />
<Button layoutX="432.0" layoutY="70.0" mnemonicParsing="false" onAction="#performQuantizePass" prefHeight="26.0" prefWidth="141.0" text="Quantize Pass" AnchorPane.rightAnchor="9.0" />
<Button layoutX="432.0" layoutY="101.0" mnemonicParsing="false" onAction="#performDiffusionPass" text="Error Diffusion Pass" AnchorPane.rightAnchor="9.0" />
<Label layoutX="434.0" layoutY="13.0" text="Fill with" AnchorPane.rightAnchor="98.0" />
<TextField fx:id="fillValue" layoutX="484.0" layoutY="8.0" prefHeight="26.0" prefWidth="44.0" text="0" AnchorPane.rightAnchor="54.0" />
<Button layoutX="531.0" layoutY="8.0" mnemonicParsing="false" onAction="#fillOutput" text="Fill!" AnchorPane.rightAnchor="9.0" />
<Button layoutX="513.0" layoutY="39.0" mnemonicParsing="false" onAction="#randomizeOutput" prefHeight="26.0" prefWidth="140.0" text="Randomize" AnchorPane.rightAnchor="9.0" />
</children> </children>
</AnchorPane> </AnchorPane>
</content> </content>
</Tab> </Tab>
</tabs> </tabs>
</TabPane> </TabPane>
<Button layoutX="14.0" layoutY="438.0" mnemonicParsing="false" onAction="#performQuantizePass" prefHeight="26.0" prefWidth="141.0" text="Quantize Pass" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" />
<Button layoutX="162.0" layoutY="442.0" mnemonicParsing="false" onAction="#performDiffusionPass" text="Error Diffusion Pass" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="162.0" />
<Button layoutX="482.0" layoutY="412.0" mnemonicParsing="false" onAction="#performOK" text="OK" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="81.0" /> <Button layoutX="482.0" layoutY="412.0" mnemonicParsing="false" onAction="#performOK" text="OK" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="81.0" />
<Button layoutX="526.0" layoutY="412.0" mnemonicParsing="false" onAction="#performCancel" text="Cancel" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="14.0" /> <Button layoutX="526.0" layoutY="412.0" mnemonicParsing="false" onAction="#performCancel" text="Cancel" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="14.0" />
</children> </children>