mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-07-07 14:29:16 +00:00
Added image adjustments and tweaked HGR conversion some more (and fixed another bug...)
This commit is contained in:
parent
489a05803e
commit
b11da29c56
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user