画布到base64图像,无需awt/swing类

问题描述 投票:0回答:1

我现在尝试将 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);
java javafx canvas gluon substrate
1个回答
0
投票

您可以将像素数据编码为位图,然后将位图数据编码为数据 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
                        );
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.