我现在尝试将 Canvas 转换为 Base64 图像字符串一段时间,因此我可以稍后将其转换回来以在屏幕上显示或将其存储在文件中。 问题是,到目前为止我发现的所有示例都使用
SwingFXUtils.fromFXImage()
或其他 AWT 相关类。
我构建的应用程序将使用 Gluon Substrate 编译器来创建移动应用程序,该应用程序不支持所有这些类。那么有没有一种方法可以在没有任何 AWT/Swing 相关类的情况下做到这一点?
我到目前为止,得到了一个应用程序/八位字节流,但我需要一个图像 MIME 类型:
Canvas _canvas;
WritableImage image = new WritableImage((int) _canvas.getWidth(), (int) _canvas.getHeight());
_canvas.snapshot(new SnapshotParameters(), image);
int w = (int) image.getWidth();
int h = (int) image.getHeight();
byte[] buf = new byte[w * h * 4];
image.getPixelReader().getPixels(0, 0, w, h, PixelFormat.getByteBgraInstance(), buf, 0, w * 4);
String base64_image = McUtils.toBase64(buf);
您可以将像素数据编码为位图,然后将位图数据编码为数据 uri。
bmp 编码算法改编自 Philipp C. Heckel 的此算法
BmpEncoderApp.java
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import java.io.IOException;
public class BmpEncoderApp extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws IOException {
// display a group to snapshot.
Group root = new Group(
new Circle(
10, 10, 10, Color.GREEN
)
);
// snapshot the group.
Scene scene = new Scene(root);
Image snapshot = scene.snapshot(null);
// display the snapshot.
ImageView snapshotImageView = new ImageView(snapshot);
snapshotImageView.setTranslateX(25);
root.getChildren().add(snapshotImageView);
// encode the image as a base 64 encoded bitmap data URI.
String dataUri = BmpUriEncoder.encode(snapshot);
// display the data uri in an image view.
ImageView uriImageView = new ImageView(dataUri);
uriImageView.setTranslateX(50);
root.getChildren().add(uriImageView);
stage.setScene(scene);
stage.show();
}
}
BmpUriEncoder.java
import javafx.scene.image.Image;
import javafx.scene.image.PixelFormat;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class BmpUriEncoder {
private static final int BMP_HEADER_LENGTH = 54;
private static final int BMP_OFFSET_FILESIZE_BYTES = 2;
private static final int BMP_OFFSET_IMAGE_WIDTH = 18;
private static final int BMP_OFFSET_IMAGE_HEIGHT = 22;
private static final int BMP_OFFSET_IMAGE_DATA_BYTES = 34;
private static final int BMP_OFFSET_PAYLOAD_LENGTH = 38;
private static final byte UDEF = 0x00;
private static final byte[] BMP_HEADER = new byte[] {
/* 00 */0x42, 0x4d, // signature
/* 02 */UDEF, UDEF, UDEF, UDEF, // size in bytes, filled dynamically
/* 06 */0x00, 0x00, // reserved, must be zero
/* 08 */0x00, 0x00, // reserved, must be zero
/* 10 */0x36, 0x00, 0x00, 0x00, // offset to start of image data in bytes
/* 14 */0x28, 0x00, 0x00, 0x00, // size of BITMAPINFOHEADER structure, must be 40 (0x28)
/* 18 */UDEF, UDEF, UDEF, UDEF, // image width in pixels, filled dynamically
/* 22 */UDEF, UDEF, UDEF, UDEF, // image height in pixels, filled dynamically
/* 26 */0x01, 0x00, // number of planes, must be 1
/* 28 */0x20, 0x00, // number of bits per pixel (1, 4, 8 or 24) -> 24 = 0x18
/* 30 */0x00, 0x00, 0x00, 0x00, // compression type (0=none, 1=RLE-8, 2=RLE-4)
/* 34 */UDEF, UDEF, UDEF, UDEF, // size of image data in bytes (including padding)
/* 38 */UDEF, UDEF, UDEF, UDEF, // normally: horizontal resolution in pixels per meter (unreliable)
// HERE: used to indicate the payload length
/* 42 */0x00, 0x00, 0x00, 0x00, // vertical resolution in pixels per meter (unreliable)
/* 46 */0x00, 0x00, 0x00, 0x00, // number of colors in image, or zero
/* 50 */0x00, 0x00, 0x00, 0x00, // number of important colors, or zero
};
private static byte[] encodeToBitmap(
byte[] imageBytes,
int w,
int h
) throws IOException {
int payloadLength = imageBytes.length;
int filesizeBytes = w * h * 4 + BMP_HEADER_LENGTH; /* RGBA no line or file padding */
// System.out.println("payload = " + payloadLength);
// System.out.println("pixel width = " + w);
// System.out.println("pixel height = " + h);
// System.out.println("filesize total = " + filesizeBytes);
byte[] header = BMP_HEADER.clone();
writeIntLE(header, BMP_OFFSET_FILESIZE_BYTES, filesizeBytes);
writeIntLE(header, BMP_OFFSET_IMAGE_WIDTH, w);
writeIntLE(header, BMP_OFFSET_IMAGE_HEIGHT, h);
writeIntLE(header, BMP_OFFSET_IMAGE_DATA_BYTES, payloadLength);
writeIntLE(header, BMP_OFFSET_PAYLOAD_LENGTH, payloadLength);
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(header, 0, header.length);
os.write(imageBytes);
os.close();
return os.toByteArray();
}
private static void writeIntLE(byte[] bytes, int startoffset, int value) {
bytes[startoffset] = (byte) (value);
bytes[startoffset + 1] = (byte) (value >>> 8);
bytes[startoffset + 2] = (byte) (value >>> 16);
bytes[startoffset + 3] = (byte) (value >>> 24);
}
public static String encode(Image image) throws IOException {
int w = (int) image.getWidth();
int h = (int) image.getHeight();
int l = w * h * 4;
byte[] pixelData = new byte[l];
image.getPixelReader().getPixels(
0, 0,
w, h,
PixelFormat.getByteBgraInstance(),
pixelData,
0,
w * 4
);
// encode to a bitmap and save to an output stream.
byte[] bitmapData = encodeToBitmap(pixelData, w, h);
// base 64 encode the output.
byte[] base64Data = Base64.getEncoder().encode(
bitmapData
);
// encapsulate base64 encoded data in a data uri.
return
"data:image/bmp;base64," +
new String(
base64Data,
StandardCharsets.US_ASCII
);
}
}