Я пишу простое приложение, и одной из функций является возможность сериализации объекта в изображение, чтобы пользователь мог поделиться фотографией своего проекта со встроенными в него инструкциями по сборке. В настоящее время другой пользователь может перетащить изображение в JavaFX ListView, и слушатель выполнит некоторый код для декодирования и анализа встроенного JSON.
. Это прекрасно работает, если изображение хранится локально, но Я также хочу позволить пользователям перетаскивать изображения из браузера без необходимости сначала сохранять их локально. Чтобы проверить это, я связал рабочее изображение в файле basi c HTML, чтобы я мог отобразить его в Chrome.
Первоначально я использовал путь к файлу, взятому из Dragboard, но когда изображение поступает из браузера, мне нужно (я думаю), чтобы иметь возможность принять изображение напрямую, отсюда перегруженный декодер () метод ниже.
Проблема, с которой я сталкиваюсь, заключается в том, что я получаю два немного разных байтовых массива в зависимости от того, поступает ли изображение из браузера или из моего основного локального хранилища. Я недостаточно знаю об изображениях в Java (или вообще), чтобы понять, почему это так и не смог найти ответы где-либо еще. Перетаскивание из источника в браузере создает достаточно другой байтовый массив, поэтому я не могу правильно декодировать сообщение на 100% и, следовательно, не могу десериализовать его в объект. Однако, если я щелкну правой кнопкой мыши и сохраню изображение на основе браузера, оно загрузится правильно.
Я включил воспроизводимый фрагмент и тестовое изображение ниже. Мой JDK является самой последней версией Amazon Corretto 8.
Воспроизводимый фрагмент:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.image.Image;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelReader;
import javafx.scene.image.WritablePixelFormat;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.List;
public class MainApp extends Application {
private static final int OFFSET = 40;
private ListView<String> listView;
private GridPane gridPane;
@Override
public void start(Stage primaryStage) throws Exception {
listView = new ListView<>();
gridPane = new GridPane();
gridPane.addRow(0, listView);
setDragDropAction();
Scene scene = new Scene(gridPane, 250, 250);
primaryStage.setScene(scene);
primaryStage.sizeToScene();
primaryStage.show();
}
// the same drag-drop logic I am using in my project
private void setDragDropAction() {
gridPane.setOnDragOver(event -> {
if (event.getDragboard().hasFiles() || event.getDragboard().hasImage()) {
event.acceptTransferModes(TransferMode.ANY);
}
event.consume();
});
gridPane.setOnDragDropped(event -> {
List<File> files = event.getDragboard().getFiles();
try {
if (!files.isEmpty()) {
String decoded = decode(files.get(0));
System.out.println(decoded);
} else if (event.getDragboard().hasImage()) {
Image image = event.getDragboard().getImage();
String decoded = decode(image);
System.out.println(decoded);
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
// decode using the file path
public String decode(File file) throws IOException {
byte[] byteImage = imageToByteArray(file);
return getStringFromBytes(byteImage);
}
// this results in a different byte array and a failed decode
public String decode(Image image) {
int width = (int) image.getWidth();
int height = (int) image.getHeight();
PixelReader reader = image.getPixelReader();
byte[] byteImage = new byte[width * height * 4];
WritablePixelFormat<ByteBuffer> format = PixelFormat.getByteBgraInstance();
reader.getPixels(0, 0, width, height, format, byteImage, 0, width * 4);
return getStringFromBytes(byteImage);
}
private String getStringFromBytes(byte[] byteImage) {
int offset = OFFSET;
byte[] byteLength = new byte[4];
System.arraycopy(byteImage, 1, byteLength, 0, (offset / 8) - 1);
int length = byteArrayToInt(byteLength);
byte[] result = new byte[length];
for (int b = 0; b < length; ++b) {
for (int i = 0; i < 8; ++i, ++offset) {
result[b] = (byte) ((result[b] << 1) | (byteImage[offset] & 1));
}
}
return new String(result);
}
private int byteArrayToInt(byte[] b) {
return b[3] & 0xFF
| (b[2] & 0xFF) << 8
| (b[1] & 0xFF) << 16
| (b[0] & 0xFF) << 24;
}
private byte[] imageToByteArray(File file) throws IOException {
BufferedImage image;
URL path = file.toURI().toURL();
image = ImageIO.read(path);
return ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
}
}
Еще одна попытка метода декодирования с использованием SwingFXUtils.fromFXImage (), которая также не работает:
// Create a buffered image with the same imageType (6) as the type returned from ImageIO.read() with a local drag-drop
public String decode(Image image) throws IOException {
int width = (int) image.getWidth();
int height = (int) image.getHeight();
BufferedImage buffImageFinal = new BufferedImage(width, height, 6); // imageType = 6
BufferedImage buffImageTemp = SwingFXUtils.fromFXImage(image, null);
Graphics2D g = buffImageFinal.createGraphics();
g.drawImage(buffImageTemp, 0, 0, width, height, null);
g.dispose();
return getStringFromBytes(((DataBufferByte) buffImageFinal.getRaster().getDataBuffer()).getData());
}
Изображения, показывающие изменения в данных:
Угол изображения. Разные пиксели выделены красным.
![First 12 bytes showing the difference](https://i.stack.imgur.com/ykVIM.png)
Пример изображения для тестирования:
![Example image for testing](https://i.stack.imgur.com/ZOfm3.png)
Декодированное тестовое сообщение должно print out («Это тестовое сообщение для моего минимального воспроизводимого примера». Перетаскивание изображения из браузера не работает, сохранение изображения локально и перетаскивание -.