使用libwebp dll和JNA解码动画图像

发布于 2025-02-13 23:11:44 字数 17585 浏览 1 评论 0 原文

我需要在Java中阅读静态和动画的WebP图像。 libwepb ,我已经下载了 source> source 并在本机工具命令提示在这样的窗口上为vs 2022 vs 2022:

..\libwebp-1.2.2>nmake /f Makefile.vc CFG=release-dynamic RTLIBCFG=dynamic OBJDIR=output

这导致了文件 libwebp.dll libwebpdecoder.dll libwebpdemux.dll 。我正在为JNA使用 JNA-5.12.1.JAR 。我能够使用 libwebp.dll 和函数 webpdecodergba 来解码静态图像,但是解码动画映像不适用。据我了解, libwebpdemux.dll 中的demux函数是必需的。

我试图实现

WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options);

来自 documentation 它看起来像 dec_options 可能只是默认值为默认值,但我不确定它想要 webp_data 。我试图实现,但我不确定这是否正确。

@Structure.FieldOrder({ "bytes", "length" })
public static class WebPData extends Structure {
    public byte[] bytes;
    public int length;
}

我只需直接分配字节> data.bytes = rowdata; 即可获得“无效的内存访问”异常。在WebP struct定义上,它说“必须使用 webpmalloc()等等。不知何故,但是我不知道这将如何工作,如何将数据获取?现在,它的方式抛出了一个异常,抱怨返回类型。

令我感到困惑的是,调用 webpanimdecodernew ,但是该功能没有导出,因此我无法访问它。相反,有一个称为 带有其他int参数,显然应该接收 webp_demux_abi_version 常数。我不太确定那是什么。我真的应该称呼“内部”功能?

这就是我一直在测试的内容(一些评论中的更多信息):

package webptest;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Hashtable;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class WebPDecodingTest {

    public static void main(String[] args) throws Exception {
        System.setProperty("jna.library.path", "<path>\\lib\\x64");
        
        // This only shows the first image
        ImageIcon staticImage = decodeStatic("https://www.gstatic.com/webp/gallery/4.sm.webp");
        ImageIcon animatedImage = decodeStatic("https://storage.googleapis.com/downloads.webmproject.org/webp/images/dancing_banana2.lossless.webp");
        showImages(staticImage, animatedImage);
        
        // This throws an error (and it's incomplete anyway)
        ImageIcon[] animatedImage2 = decodeAnimated("https://storage.googleapis.com/downloads.webmproject.org/webp/images/dancing_banana2.lossless.webp");
        showImages(animatedImage2);
    }

    //==========================
    // Animated image
    //==========================
    
    /**
     * Decode the frames of an animated image. Doesn't work and incomplete.
     */
    private static ImageIcon[] decodeAnimated(String url) throws Exception {
        byte[] rawData = getBytesFromURL(new URL(url));
        System.out.println("Bytes length: "+rawData.length);

        /**
         * I assume that this is already not correct. In a C example
         * (examples/anim_util.c) it uses "WebPDataInit(&webp_data);" first, but
         * that function isn't exported. It's in "src/webp/mux_types.h":
         * 
             // Initializes the contents of the 'webp_data' object with default values.
            static WEBP_INLINE void WebPDataInit(WebPData* webp_data) {
              if (webp_data != NULL) {
                memset(webp_data, 0, sizeof(*webp_data));
              }
            }
         * 
         * I tried just assigning the data direclty like "data.bytes = rawdata;"
         * however that resulted in a "Invalid memory access" exception when
         * calling "WebPAnimDecoderNewInternal".
         * 
         * At the definition of the WebPData struct (see above) it says
         * "'bytes' memory must be allocated using WebPMalloc() and such."
         * so I assume that might be required to be used, but I'm not sure how.
         * 
         * The way it is now throws a "Unsupported return type class [B in
         * function WebPMalloc" exception.
         */
        LibWebPDemux.WebPData data = new LibWebPDemux.WebPData();
        data.bytes = LibWebP.INSTANCE.WebPMalloc(rawData.length);
        data.length = rawData.length;

        // This may not be required, instead "null" can be provided for default options
//        LibWebPDemux.WebPAnimDecoderOptions options = new LibWebPDemux.WebPAnimDecoderOptions();
//        int initResult = LibWebPDemux.INSTANCE.WebPAnimDecoderOptionsInitInternal(options, LibWebPDemux.WEBP_DEMUX_ABI_VERSION);
//        System.out.println(initResult);

        Pointer dec = LibWebPDemux.INSTANCE.WebPAnimDecoderNewInternal(data, null, LibWebPDemux.WEBP_DEMUX_ABI_VERSION);
        System.out.println(dec);
        
        // Just testing if accessing the lib works at all (it appears to)
//        int version = LibWebPDemux.INSTANCE.WebPGetDemuxVersion();
//        System.out.println(version);
        return null;
    }
    
    //==========================
    // libwebp
    //==========================
    public interface LibWebP extends Library {

        LibWebP INSTANCE = Native.load("libwebp", LibWebP.class);

        /*
        [webp/decode.h]
            // Retrieve basic header information: width, height.
            // This function will also validate the header, returning true on success,
            // false otherwise. '*width' and '*height' are only valid on successful return.
            // Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
            // Note: The following chunk sequences (before the raw VP8/VP8L data) are
            // considered valid by this function:
            // RIFF + VP8(L)
            // RIFF + VP8X + (optional chunks) + VP8(L)
            // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
            // VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
            WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size,
                                        int* width, int* height);
        */
        public int WebPGetInfo(byte[] data, int data_size, IntByReference width, IntByReference height);
        
        /*
        [webp/decode.h]
            // Decodes WebP images pointed to by 'data' and returns RGBA samples, along
            // with the dimensions in *width and *height. The ordering of samples in
            // memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
            // The returned pointer should be deleted calling WebPFree().
            // Returns NULL in case of error.
            WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
                                                int* width, int* height);
        */
        public Pointer WebPDecodeRGBA(byte[] data, int data_size, IntByReference width, IntByReference height);
        
        /*
        [webp/types.h]
            // Allocates 'size' bytes of memory. Returns NULL upon error. Memory
            // must be deallocated by calling WebPFree(). This function is made available
            // by the core 'libwebp' library.
            WEBP_EXTERN void* WebPMalloc(size_t size);
        */
        public byte[] WebPMalloc(int size);
        
        /*
        [webp/types.h]
            // Releases memory returned by the WebPDecode*() functions (from decode.h).
            WEBP_EXTERN void WebPFree(void* ptr);
        */
        public void WebPFree(Pointer pointer);
    }
    
    //==========================
    // libwebpdemux
    //==========================
    public interface LibWebPDemux extends Library {
        LibWebPDemux INSTANCE = Native.load("libwebpdemux", LibWebPDemux.class);
        
        static final int WEBP_DEMUX_ABI_VERSION = 0x0107;
        
        /*
        [webp/demux.h]
            // Internal, version-checked, entry point.
            WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
                const WebPData*, const WebPAnimDecoderOptions*, int);

            // Creates and initializes a WebPAnimDecoder object.
            // Parameters:
            //   webp_data - (in) WebP bitstream. This should remain unchanged during the
            //                    lifetime of the output WebPAnimDecoder object.
            //   dec_options - (in) decoding options. Can be passed NULL to choose
            //                      reasonable defaults (in particular, color mode MODE_RGBA
            //                      will be picked).
            // Returns:
            //   A pointer to the newly created WebPAnimDecoder object, or NULL in case of
            //   parsing error, invalid option or memory error.
            static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
                const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) {
              return WebPAnimDecoderNewInternal(webp_data, dec_options,
                                                WEBP_DEMUX_ABI_VERSION);
            }
        */
        public Pointer WebPAnimDecoderNewInternal(WebPData webp_data, Structure dec_options, int version);
        
        /*
        [webp/mux_types.h]
            // Data type used to describe 'raw' data, e.g., chunk data
            // (ICC profile, metadata) and WebP compressed image data.
            // 'bytes' memory must be allocated using WebPMalloc() and such.
            struct WebPData {
              const uint8_t* bytes;
              size_t size;
            };
        */
        @Structure.FieldOrder({ "bytes", "length" })
        public static class WebPData extends Structure {
            public byte[] bytes;
            public int length;
        }
        
        /*
        [webp/demux.h]
            // Returns the version number of the demux library, packed in hexadecimal using
            // 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
            WEBP_EXTERN int WebPGetDemuxVersion(void);
        */
        public int WebPGetDemuxVersion();
        
        /*
        [webp/demux.h]
            // Internal, version-checked, entry point.
            WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal(
                WebPAnimDecoderOptions*, int);

            // Should always be called, to initialize a fresh WebPAnimDecoderOptions
            // structure before modification. Returns false in case of version mismatch.
            // WebPAnimDecoderOptionsInit() must have succeeded before using the
            // 'dec_options' object.
            static WEBP_INLINE int WebPAnimDecoderOptionsInit(
                WebPAnimDecoderOptions* dec_options) {
              return WebPAnimDecoderOptionsInitInternal(dec_options,
                                                        WEBP_DEMUX_ABI_VERSION);
            }
        */
        public int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions options, int version);
        
        /*
        [webp/demux.h]
            // Global options.
            struct WebPAnimDecoderOptions {
              // Output colorspace. Only the following modes are supported:
              // MODE_RGBA, MODE_BGRA, MODE_rgbA and MODE_bgrA.
              WEBP_CSP_MODE color_mode;
              int use_threads;           // If true, use multi-threaded decoding.
              uint32_t padding[7];       // Padding for later use.
            };
        
        [webp/decode.h]
            typedef enum WEBP_CSP_MODE {
              MODE_RGB = 0, MODE_RGBA = 1,
              MODE_BGR = 2, MODE_BGRA = 3,
              MODE_ARGB = 4, MODE_RGBA_4444 = 5,
              MODE_RGB_565 = 6,
              // RGB-premultiplied transparent modes (alpha value is preserved)
              MODE_rgbA = 7,
              MODE_bgrA = 8,
              MODE_Argb = 9,
              MODE_rgbA_4444 = 10,
              // YUV modes must come after RGB ones.
              MODE_YUV = 11, MODE_YUVA = 12,  // yuv 4:2:0
              MODE_LAST = 13
            } WEBP_CSP_MODE;
        */
        @Structure.FieldOrder({ "color_mode", "use_threads", "padding" })
        public static class WebPAnimDecoderOptions extends Structure {
            public int color_mode; // enum
            public int use_threads;
            public int[] padding = new int[7];
        }
        
        /*
        [webp/demux.h]
            // Global information about the animation..
            struct WebPAnimInfo {
              uint32_t canvas_width;
              uint32_t canvas_height;
              uint32_t loop_count;
              uint32_t bgcolor;
              uint32_t frame_count;
              uint32_t pad[4];   // padding for later use
            };
        */
        @Structure.FieldOrder({ "canvas_width", "canvas_height", "loop_count", "bgcolor", "frame_count", "pad" })
        public static class WebPAnimInfo extends Structure {
            public int canvas_width;
            public int canvas_height;
            public int loop_count;
            public int bgcolor;
            public int frame_count;
            public int[] pad = new int[4];
        }
    }
    
    //==========================
    // Static image
    //==========================
    
    /**
     * Decoding a static WebP image. I'm not sure if it's entirely correct, but
     * at least an image comes out of it.
     */
    private static ImageIcon decodeStatic(String url) throws Exception {
        byte[] rawData = getBytesFromURL(new URL(url));
        IntByReference widthRef = new IntByReference();
        IntByReference heightRef = new IntByReference();
        int result = LibWebP.INSTANCE.WebPGetInfo(rawData, rawData.length, widthRef, heightRef);
        if (result == 1) {
//            System.out.println(widthRef.getValue() + " " + heightRef.getValue());

            Pointer pixelData = LibWebP.INSTANCE.WebPDecodeRGBA(rawData, rawData.length, widthRef, heightRef);
            if (pixelData != null) {
                int width = widthRef.getValue();
                int height = heightRef.getValue();

                int[] pixels = pixelData.getIntArray(0, width * height);

                ColorModel colorModel;
                colorModel = new DirectColorModel(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);

                SampleModel sampleModel = colorModel.createCompatibleSampleModel(width, height);
                DataBufferInt db = new DataBufferInt(pixels, width * height);
                WritableRaster raster = WritableRaster.createWritableRaster(sampleModel, db, null);

                Image img = new BufferedImage(colorModel, raster, false, new Hashtable<Object, Object>());
                return new ImageIcon(img);
            }
        }
        System.out.println("Not a valid WebP");
        return null;
    }
    
    //==========================
    // General Helpers
    //==========================
    private static void showImages(ImageIcon... icons) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            frame.setLayout(new FlowLayout());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            for (ImageIcon icon : icons) {
                frame.add(new JLabel(icon), BorderLayout.CENTER);
            }
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
    
    public static byte[] getBytesFromURL(URL url) throws Exception {
        URLConnection c = url.openConnection();
        try (InputStream input = c.getInputStream()) {
            byte[] imageData = readAllBytes(input);
            return imageData;
        }
    }
    
    private static byte[] readAllBytes(InputStream input) throws IOException {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = input.read(buffer, 0, buffer.length)) != -1) {
            result.write(buffer, 0, length);
        }
        return result.toByteArray();
    }    
}

I need to read static and animated WebP images in Java. There is no DLL offered for libwepb, so I've downloaded the source and compiled it in the Native Tools Command Prompt for VS 2022 on Windows like this:

..\libwebp-1.2.2>nmake /f Makefile.vc CFG=release-dynamic RTLIBCFG=dynamic OBJDIR=output

This resulted in the files libwebp.dll, libwebpdecoder.dll and libwebpdemux.dll. I'm using jna-5.12.1.jar for JNA. I was able to decode a static image using the libwebp.dll and the function WebPDecodeRGBA, however decoding an animated image doesn't work with that. From what I understand the demux functions in libwebpdemux.dll are required for that.

I tried to implement the WebPAnimDecoder API, but I'm already stuck at creating the decoder:

WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options);

From the documentation it looks like dec_options can just be null to get a default, but I'm not sure how it wants the webp_data. I tried to implement the WebPData struct, but I'm not sure if that is even correct.

@Structure.FieldOrder({ "bytes", "length" })
public static class WebPData extends Structure {
    public byte[] bytes;
    public int length;
}

I've gotten an "Invalid memory access" exception by just assigning the bytes directly data.bytes = rawdata;. At the WebP struct definition it says "'bytes' memory must be allocated using WebPMalloc() and such.", so I assume that is required somehow, but I don't know how that would work, how do I get the data in there? The way it is now throws an exception complaining about the return type.

What also confuses me is that the example calls WebPAnimDecoderNew, but that function is not exported, so I can't access it. Instead, there is a function called WebPAnimDecoderNewInternal with an additional int parameter that apparently is supposed to receive the WEBP_DEMUX_ABI_VERSION constant. I'm not quite sure what the signficance of that is. Am I really supposed to call the "Internal" function?

This is what I've been testing with (more information in some comments):

package webptest;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Hashtable;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class WebPDecodingTest {

    public static void main(String[] args) throws Exception {
        System.setProperty("jna.library.path", "<path>\\lib\\x64");
        
        // This only shows the first image
        ImageIcon staticImage = decodeStatic("https://www.gstatic.com/webp/gallery/4.sm.webp");
        ImageIcon animatedImage = decodeStatic("https://storage.googleapis.com/downloads.webmproject.org/webp/images/dancing_banana2.lossless.webp");
        showImages(staticImage, animatedImage);
        
        // This throws an error (and it's incomplete anyway)
        ImageIcon[] animatedImage2 = decodeAnimated("https://storage.googleapis.com/downloads.webmproject.org/webp/images/dancing_banana2.lossless.webp");
        showImages(animatedImage2);
    }

    //==========================
    // Animated image
    //==========================
    
    /**
     * Decode the frames of an animated image. Doesn't work and incomplete.
     */
    private static ImageIcon[] decodeAnimated(String url) throws Exception {
        byte[] rawData = getBytesFromURL(new URL(url));
        System.out.println("Bytes length: "+rawData.length);

        /**
         * I assume that this is already not correct. In a C example
         * (examples/anim_util.c) it uses "WebPDataInit(&webp_data);" first, but
         * that function isn't exported. It's in "src/webp/mux_types.h":
         * 
             // Initializes the contents of the 'webp_data' object with default values.
            static WEBP_INLINE void WebPDataInit(WebPData* webp_data) {
              if (webp_data != NULL) {
                memset(webp_data, 0, sizeof(*webp_data));
              }
            }
         * 
         * I tried just assigning the data direclty like "data.bytes = rawdata;"
         * however that resulted in a "Invalid memory access" exception when
         * calling "WebPAnimDecoderNewInternal".
         * 
         * At the definition of the WebPData struct (see above) it says
         * "'bytes' memory must be allocated using WebPMalloc() and such."
         * so I assume that might be required to be used, but I'm not sure how.
         * 
         * The way it is now throws a "Unsupported return type class [B in
         * function WebPMalloc" exception.
         */
        LibWebPDemux.WebPData data = new LibWebPDemux.WebPData();
        data.bytes = LibWebP.INSTANCE.WebPMalloc(rawData.length);
        data.length = rawData.length;

        // This may not be required, instead "null" can be provided for default options
//        LibWebPDemux.WebPAnimDecoderOptions options = new LibWebPDemux.WebPAnimDecoderOptions();
//        int initResult = LibWebPDemux.INSTANCE.WebPAnimDecoderOptionsInitInternal(options, LibWebPDemux.WEBP_DEMUX_ABI_VERSION);
//        System.out.println(initResult);

        Pointer dec = LibWebPDemux.INSTANCE.WebPAnimDecoderNewInternal(data, null, LibWebPDemux.WEBP_DEMUX_ABI_VERSION);
        System.out.println(dec);
        
        // Just testing if accessing the lib works at all (it appears to)
//        int version = LibWebPDemux.INSTANCE.WebPGetDemuxVersion();
//        System.out.println(version);
        return null;
    }
    
    //==========================
    // libwebp
    //==========================
    public interface LibWebP extends Library {

        LibWebP INSTANCE = Native.load("libwebp", LibWebP.class);

        /*
        [webp/decode.h]
            // Retrieve basic header information: width, height.
            // This function will also validate the header, returning true on success,
            // false otherwise. '*width' and '*height' are only valid on successful return.
            // Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
            // Note: The following chunk sequences (before the raw VP8/VP8L data) are
            // considered valid by this function:
            // RIFF + VP8(L)
            // RIFF + VP8X + (optional chunks) + VP8(L)
            // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
            // VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
            WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size,
                                        int* width, int* height);
        */
        public int WebPGetInfo(byte[] data, int data_size, IntByReference width, IntByReference height);
        
        /*
        [webp/decode.h]
            // Decodes WebP images pointed to by 'data' and returns RGBA samples, along
            // with the dimensions in *width and *height. The ordering of samples in
            // memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
            // The returned pointer should be deleted calling WebPFree().
            // Returns NULL in case of error.
            WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
                                                int* width, int* height);
        */
        public Pointer WebPDecodeRGBA(byte[] data, int data_size, IntByReference width, IntByReference height);
        
        /*
        [webp/types.h]
            // Allocates 'size' bytes of memory. Returns NULL upon error. Memory
            // must be deallocated by calling WebPFree(). This function is made available
            // by the core 'libwebp' library.
            WEBP_EXTERN void* WebPMalloc(size_t size);
        */
        public byte[] WebPMalloc(int size);
        
        /*
        [webp/types.h]
            // Releases memory returned by the WebPDecode*() functions (from decode.h).
            WEBP_EXTERN void WebPFree(void* ptr);
        */
        public void WebPFree(Pointer pointer);
    }
    
    //==========================
    // libwebpdemux
    //==========================
    public interface LibWebPDemux extends Library {
        LibWebPDemux INSTANCE = Native.load("libwebpdemux", LibWebPDemux.class);
        
        static final int WEBP_DEMUX_ABI_VERSION = 0x0107;
        
        /*
        [webp/demux.h]
            // Internal, version-checked, entry point.
            WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
                const WebPData*, const WebPAnimDecoderOptions*, int);

            // Creates and initializes a WebPAnimDecoder object.
            // Parameters:
            //   webp_data - (in) WebP bitstream. This should remain unchanged during the
            //                    lifetime of the output WebPAnimDecoder object.
            //   dec_options - (in) decoding options. Can be passed NULL to choose
            //                      reasonable defaults (in particular, color mode MODE_RGBA
            //                      will be picked).
            // Returns:
            //   A pointer to the newly created WebPAnimDecoder object, or NULL in case of
            //   parsing error, invalid option or memory error.
            static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
                const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) {
              return WebPAnimDecoderNewInternal(webp_data, dec_options,
                                                WEBP_DEMUX_ABI_VERSION);
            }
        */
        public Pointer WebPAnimDecoderNewInternal(WebPData webp_data, Structure dec_options, int version);
        
        /*
        [webp/mux_types.h]
            // Data type used to describe 'raw' data, e.g., chunk data
            // (ICC profile, metadata) and WebP compressed image data.
            // 'bytes' memory must be allocated using WebPMalloc() and such.
            struct WebPData {
              const uint8_t* bytes;
              size_t size;
            };
        */
        @Structure.FieldOrder({ "bytes", "length" })
        public static class WebPData extends Structure {
            public byte[] bytes;
            public int length;
        }
        
        /*
        [webp/demux.h]
            // Returns the version number of the demux library, packed in hexadecimal using
            // 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
            WEBP_EXTERN int WebPGetDemuxVersion(void);
        */
        public int WebPGetDemuxVersion();
        
        /*
        [webp/demux.h]
            // Internal, version-checked, entry point.
            WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal(
                WebPAnimDecoderOptions*, int);

            // Should always be called, to initialize a fresh WebPAnimDecoderOptions
            // structure before modification. Returns false in case of version mismatch.
            // WebPAnimDecoderOptionsInit() must have succeeded before using the
            // 'dec_options' object.
            static WEBP_INLINE int WebPAnimDecoderOptionsInit(
                WebPAnimDecoderOptions* dec_options) {
              return WebPAnimDecoderOptionsInitInternal(dec_options,
                                                        WEBP_DEMUX_ABI_VERSION);
            }
        */
        public int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions options, int version);
        
        /*
        [webp/demux.h]
            // Global options.
            struct WebPAnimDecoderOptions {
              // Output colorspace. Only the following modes are supported:
              // MODE_RGBA, MODE_BGRA, MODE_rgbA and MODE_bgrA.
              WEBP_CSP_MODE color_mode;
              int use_threads;           // If true, use multi-threaded decoding.
              uint32_t padding[7];       // Padding for later use.
            };
        
        [webp/decode.h]
            typedef enum WEBP_CSP_MODE {
              MODE_RGB = 0, MODE_RGBA = 1,
              MODE_BGR = 2, MODE_BGRA = 3,
              MODE_ARGB = 4, MODE_RGBA_4444 = 5,
              MODE_RGB_565 = 6,
              // RGB-premultiplied transparent modes (alpha value is preserved)
              MODE_rgbA = 7,
              MODE_bgrA = 8,
              MODE_Argb = 9,
              MODE_rgbA_4444 = 10,
              // YUV modes must come after RGB ones.
              MODE_YUV = 11, MODE_YUVA = 12,  // yuv 4:2:0
              MODE_LAST = 13
            } WEBP_CSP_MODE;
        */
        @Structure.FieldOrder({ "color_mode", "use_threads", "padding" })
        public static class WebPAnimDecoderOptions extends Structure {
            public int color_mode; // enum
            public int use_threads;
            public int[] padding = new int[7];
        }
        
        /*
        [webp/demux.h]
            // Global information about the animation..
            struct WebPAnimInfo {
              uint32_t canvas_width;
              uint32_t canvas_height;
              uint32_t loop_count;
              uint32_t bgcolor;
              uint32_t frame_count;
              uint32_t pad[4];   // padding for later use
            };
        */
        @Structure.FieldOrder({ "canvas_width", "canvas_height", "loop_count", "bgcolor", "frame_count", "pad" })
        public static class WebPAnimInfo extends Structure {
            public int canvas_width;
            public int canvas_height;
            public int loop_count;
            public int bgcolor;
            public int frame_count;
            public int[] pad = new int[4];
        }
    }
    
    //==========================
    // Static image
    //==========================
    
    /**
     * Decoding a static WebP image. I'm not sure if it's entirely correct, but
     * at least an image comes out of it.
     */
    private static ImageIcon decodeStatic(String url) throws Exception {
        byte[] rawData = getBytesFromURL(new URL(url));
        IntByReference widthRef = new IntByReference();
        IntByReference heightRef = new IntByReference();
        int result = LibWebP.INSTANCE.WebPGetInfo(rawData, rawData.length, widthRef, heightRef);
        if (result == 1) {
//            System.out.println(widthRef.getValue() + " " + heightRef.getValue());

            Pointer pixelData = LibWebP.INSTANCE.WebPDecodeRGBA(rawData, rawData.length, widthRef, heightRef);
            if (pixelData != null) {
                int width = widthRef.getValue();
                int height = heightRef.getValue();

                int[] pixels = pixelData.getIntArray(0, width * height);

                ColorModel colorModel;
                colorModel = new DirectColorModel(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);

                SampleModel sampleModel = colorModel.createCompatibleSampleModel(width, height);
                DataBufferInt db = new DataBufferInt(pixels, width * height);
                WritableRaster raster = WritableRaster.createWritableRaster(sampleModel, db, null);

                Image img = new BufferedImage(colorModel, raster, false, new Hashtable<Object, Object>());
                return new ImageIcon(img);
            }
        }
        System.out.println("Not a valid WebP");
        return null;
    }
    
    //==========================
    // General Helpers
    //==========================
    private static void showImages(ImageIcon... icons) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            frame.setLayout(new FlowLayout());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            for (ImageIcon icon : icons) {
                frame.add(new JLabel(icon), BorderLayout.CENTER);
            }
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
    
    public static byte[] getBytesFromURL(URL url) throws Exception {
        URLConnection c = url.openConnection();
        try (InputStream input = c.getInputStream()) {
            byte[] imageData = readAllBytes(input);
            return imageData;
        }
    }
    
    private static byte[] readAllBytes(InputStream input) throws IOException {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = input.read(buffer, 0, buffer.length)) != -1) {
            result.write(buffer, 0, length);
        }
        return result.toByteArray();
    }    
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

舞袖。长 2025-02-20 23:11:46

JNA结构需要适当分配其尺寸信息,因此该映射应该给您一个错误,而不指定字节数组大小:

@Structure.FieldOrder({ "bytes", "length" })
public static class WebPData extends Structure {
    public byte[] bytes;
    public int length;
}

由于您已指示字节是在其他地方分配的,因此,阵列的指针应仅以<<<代码>指针。另外,长度的类型 size_t ,因此您需要将该映射用于 *nix或Windows的 size_t 映射。

@Structure.FieldOrder({ "bytes", "length" })
public static class WebPData extends Structure {
    public Pointer bytes;
    public BaseTSD.SIZE_T length;
}

然后,您可以手动分配字节。您指出 webpmalloc()这样做,但我不知道这与仅使用 malloc()或等效的情况有何不同,如果您使用JNA的新内存(大小)。如果使用该函数,则将长度值作为参数传递给该函数,返回值将是指针,您将将其设置为 bytes

将字节设置为指向长度大小分配的指针后,您可以将结构传递为 webp_data 参数。

在您的映射 byte [] 时,本机代码试图使其起作用(因为这只是指针),但是使用 int 为长度可能没有如果 size_t 为8个字节,请更正。

JNA structures need their size information to be properly allocated, so this mapping should have given you an error without specifying the byte array size:

@Structure.FieldOrder({ "bytes", "length" })
public static class WebPData extends Structure {
    public byte[] bytes;
    public int length;
}

Since you've indicated the bytes are allocated elsewhere, the pointer to the array should just be mapped with a Pointer. Also the type of length is size_t so you need to use that mapping for either *nix, or the SIZE_T mapping for Windows.

@Structure.FieldOrder({ "bytes", "length" })
public static class WebPData extends Structure {
    public Pointer bytes;
    public BaseTSD.SIZE_T length;
}

You could then manually allocate the bytes. You indicate WebPMalloc() does that but I don't know how that's any different than just using malloc() or equivalent which is under the hood if you use JNA's new Memory(size). If you use that function you would pass the length value as a parameter to that function and the return value would be a Pointer that you would set as bytes.

Once you've set the bytes to a pointer to the length-sized allocation, you can pass the structure as the webp_data argument.

It is possible that with your mapping of byte[] the native code tried to make it work (as that's just a pointer) but using int for the length may have not been correct if size_t was 8 bytes.

提赋 2025-02-20 23:11:46

感谢 daniel widdis的答案我更改了 webpdata 's bytes 再次将指针视为指针。我之前并不真正知道如何将我的原始数据获取到其中,不确定为什么我之前没有注意到指针的 write()方法。

webpdata 's 长度字段的类型几乎没关系(简短 int ,<代码>长, float double 都可以使用),我想它会自动将其转换为以某种方式。不确定这是否会在不同的情况/系统中破裂。

new Memory()和本机 webpmalloc()函数似乎对分配内存起作用。我已经查看了该功能的实际功能:

void* WebPMalloc(size_t size) {
  return WebPSafeMalloc(1, size);
}

void* WebPSafeMalloc(uint64_t nmemb, size_t size) {
  void* ptr;
  Increment(&num_malloc_calls);
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
  assert(nmemb * size > 0);
  ptr = malloc((size_t)(nmemb * size));
  AddMem(ptr, (size_t)(nmemb * size));
  return ptr;
}

我不完全理解它的作用,但似乎正在进行一些其他检查和管理。不确定是否有必要使用它,但如果图书馆推荐它,也可能会使用它吗?

将数据作为指针提供后,我得到了解码器对象。我唯一遇到的问题的是获得下一个帧的功能:

WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
                                                   uint8_t** buf, int* timestamp);

这似乎是双重指针吗?因此,我必须从参考文献中获得指针,然后再从中获得指针:

PointerByReference buf = new PointerByReference();
IntByReference timestamp = new IntByReference();
int nextResult = LibWebPDemux.INSTANCE.WebPAnimDecoderGetNext(dec, buf, timestamp);
ImageIcon icon = createImage(buf.getPointer().getPointer(0), info.canvas_width, info.canvas_height);

这是更新的代码,对实际使用的内容进行了一些清理。我不确定它是否完全正确,但是我现在得到了框架和其他信息,因此至少应该不是那么错误。

它肯定需要进行一些正确的错误检查,我不确定之后是否需要清理任何记忆。

package webptest;

import com.sun.jna.Library;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.BaseTSD;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Hashtable;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class WebPDecodingTest1 {

    public static void main(String[] args) throws Exception {
        System.setProperty("jna.library.path", "<path>\\lib\\x64");
        ImageIcon[] animatedImage = decodeAnimated("https://storage.googleapis.com/downloads.webmproject.org/webp/images/dancing_banana2.lossless.webp");
        showImages(animatedImage);
    }
    
    //==========================
    // Animated image
    //==========================
    
    /**
     * Decode the frames of an animated image. Doesn't work and incomplete.
     */
    private static ImageIcon[] decodeAnimated(String url) throws Exception {
        byte[] rawData = getBytesFromURL(new URL(url));
        LibWebPDemux.WebPData data = new LibWebPDemux.WebPData();
        data.bytes = LibWebP.INSTANCE.WebPMalloc(rawData.length);
        // This worked as well
//        data.bytes = new Memory(rawData.length);

        data.bytes.write(0, rawData, 0, rawData.length);
        data.length = rawData.length;
        Pointer dec = LibWebPDemux.INSTANCE.WebPAnimDecoderNewInternal(data, null, LibWebPDemux.WEBP_DEMUX_ABI_VERSION);
        System.out.println(dec);
        
        LibWebPDemux.WebPAnimInfo info = new LibWebPDemux.WebPAnimInfo();
        LibWebPDemux.INSTANCE.WebPAnimDecoderGetInfo(dec, info);
        System.out.println(info.canvas_width+" "+info.canvas_height+" "+info.frame_count);
        ImageIcon[] result = new ImageIcon[info.frame_count];
        int frame = 0;
        while (LibWebPDemux.INSTANCE.WebPAnimDecoderHasMoreFrames(dec) == 1) {
            PointerByReference buf = new PointerByReference();
            IntByReference timestamp = new IntByReference();
            
            int nextResult = LibWebPDemux.INSTANCE.WebPAnimDecoderGetNext(dec, buf, timestamp);
            System.out.println(nextResult+" "+timestamp.getValue());
            
            ImageIcon icon = createImage(buf.getPointer().getPointer(0), info.canvas_width, info.canvas_height);
            result[frame] = icon;
            frame++;
        }
        /**
         * Not sure if this is correct or if other stuff needs to be cleared as
         * well.
         */
        LibWebPDemux.INSTANCE.WebPAnimDecoderDelete(dec);
        LibWebP.INSTANCE.WebPFree(data.bytes);
        return result;
    }
    
    //==========================
    // libwebp
    //==========================
    public interface LibWebP extends Library {

        LibWebP INSTANCE = Native.load("libwebp", LibWebP.class);
        
        /*
        [webp/types.h]
            // Allocates 'size' bytes of memory. Returns NULL upon error. Memory
            // must be deallocated by calling WebPFree(). This function is made available
            // by the core 'libwebp' library.
            WEBP_EXTERN void* WebPMalloc(size_t size);
        */
        public Pointer WebPMalloc(int size);
        
        /*
        [webp/types.h]
            // Releases memory returned by the WebPDecode*() functions (from decode.h).
            WEBP_EXTERN void WebPFree(void* ptr);
        */
        public void WebPFree(Pointer pointer);
    }
    
    //==========================
    // libwebpdemux
    //==========================
    public interface LibWebPDemux extends Library {
        LibWebPDemux INSTANCE = Native.load("libwebpdemux", LibWebPDemux.class);
        
        static final int WEBP_DEMUX_ABI_VERSION = 0x0107;
        
        /*
        [webp/demux.h]
            // Internal, version-checked, entry point.
            WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
                const WebPData*, const WebPAnimDecoderOptions*, int);

            // Creates and initializes a WebPAnimDecoder object.
            // Parameters:
            //   webp_data - (in) WebP bitstream. This should remain unchanged during the
            //                    lifetime of the output WebPAnimDecoder object.
            //   dec_options - (in) decoding options. Can be passed NULL to choose
            //                      reasonable defaults (in particular, color mode MODE_RGBA
            //                      will be picked).
            // Returns:
            //   A pointer to the newly created WebPAnimDecoder object, or NULL in case of
            //   parsing error, invalid option or memory error.
            static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
                const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) {
              return WebPAnimDecoderNewInternal(webp_data, dec_options,
                                                WEBP_DEMUX_ABI_VERSION);
            }
        */
        public Pointer WebPAnimDecoderNewInternal(WebPData webp_data, Structure dec_options, int version);
        
        /*
        [webp/mux_types.h]
            // Data type used to describe 'raw' data, e.g., chunk data
            // (ICC profile, metadata) and WebP compressed image data.
            // 'bytes' memory must be allocated using WebPMalloc() and such.
            struct WebPData {
              const uint8_t* bytes;
              size_t size;
            };
        */
        @Structure.FieldOrder({ "bytes", "length" })
        public static class WebPData extends Structure {
            public Pointer bytes;
            
            public long length;
        }
        
        /*
        [webp/demux.h]
            // Get global information about the animation.
            // Parameters:
            //   dec - (in) decoder instance to get information from.
            //   info - (out) global information fetched from the animation.
            // Returns:
            //   True on success.
            WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec,
                                                   WebPAnimInfo* info);
        */
        public int WebPAnimDecoderGetInfo(Pointer dec, WebPAnimInfo info);
        
        /*
        [webp/demux.h]
            // Global information about the animation..
            struct WebPAnimInfo {
              uint32_t canvas_width;
              uint32_t canvas_height;
              uint32_t loop_count;
              uint32_t bgcolor;
              uint32_t frame_count;
              uint32_t pad[4];   // padding for later use
            };
        */
        @Structure.FieldOrder({ "canvas_width", "canvas_height", "loop_count", "bgcolor", "frame_count", "pad" })
        public static class WebPAnimInfo extends Structure {
            public int canvas_width;
            public int canvas_height;
            public int loop_count;
            public int bgcolor;
            public int frame_count;
            public int[] pad = new int[4];
        }
        
        /*
        [webp/demux.h]
            // Check if there are more frames left to decode.
            // Parameters:
            //   dec - (in) decoder instance to be checked.
            // Returns:
            //   True if 'dec' is not NULL and some frames are yet to be decoded.
            //   Otherwise, returns false.
            WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec);
        */
        public int WebPAnimDecoderHasMoreFrames(Pointer dec);
        
        /*
        [webp/demux.h]
            // Fetch the next frame from 'dec' based on options supplied to
            // WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size
            // 'canvas_width * 4 * canvas_height', and not just the frame sub-rectangle. The
            // returned buffer 'buf' is valid only until the next call to
            // WebPAnimDecoderGetNext(), WebPAnimDecoderReset() or WebPAnimDecoderDelete().
            // Parameters:
            //   dec - (in/out) decoder instance from which the next frame is to be fetched.
            //   buf - (out) decoded frame.
            //   timestamp - (out) timestamp of the frame in milliseconds.
            // Returns:
            //   False if any of the arguments are NULL, or if there is a parsing or
            //   decoding error, or if there are no more frames. Otherwise, returns true.
            WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
                                                   uint8_t** buf, int* timestamp);
        */
        public int WebPAnimDecoderGetNext(Pointer dec, PointerByReference buf, IntByReference timestamp);
        
        /*
        [webp/demux.h]
            // Deletes the WebPAnimDecoder object.
            // Parameters:
            //   dec - (in/out) decoder instance to be deleted
            WEBP_EXTERN void WebPAnimDecoderDelete(WebPAnimDecoder* dec);
        */
        public void WebPAnimDecoderDelete(Pointer dec);
    }
    
    //==========================
    // General Helpers
    //==========================
    private static ImageIcon createImage(Pointer pixelData, int width, int height) {
        if (pixelData != null) {
            int[] pixels = pixelData.getIntArray(0, width * height);

            ColorModel colorModel;
            colorModel = new DirectColorModel(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);

            SampleModel sampleModel = colorModel.createCompatibleSampleModel(width, height);
            DataBufferInt db = new DataBufferInt(pixels, width * height);
            WritableRaster raster = WritableRaster.createWritableRaster(sampleModel, db, null);

            Image img = new BufferedImage(colorModel, raster, false, new Hashtable<Object, Object>());
            return new ImageIcon(img.getScaledInstance(100, 100, 0)); // Resized for testing
        }
        return null;
    }
    
    private static void showImages(ImageIcon... icons) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            frame.setLayout(new FlowLayout());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            for (ImageIcon icon : icons) {
                frame.add(new JLabel(icon), BorderLayout.CENTER);
            }
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
        
    }
    
    public static byte[] getBytesFromURL(URL url) throws Exception {
        URLConnection c = url.openConnection();
        try (InputStream input = c.getInputStream()) {
            byte[] imageData = readAllBytes(input);
            return imageData;
        }
    }
    
    private static byte[] readAllBytes(InputStream input) throws IOException {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = input.read(buffer, 0, buffer.length)) != -1) {
            result.write(buffer, 0, length);
        }
        return result.toByteArray();
    }
    
}

Thanks to Daniel Widdis' answer I changed the WebPData's bytes field to a Pointer again. I didn't really know before how to get my raw data into that, not sure why I didn't notice the Pointer's write() method before.

The type of the WebPData's length field almost doesn't seem to matter (short, int, long, float, double all worked), I guess it automatically converts it somehow. Not sure if that could break under different circumstances/systems though.

Both new Memory() and the native WebPMalloc() function seem to work for allocating the memory. I've looked at what the function actually does:

void* WebPMalloc(size_t size) {
  return WebPSafeMalloc(1, size);
}

void* WebPSafeMalloc(uint64_t nmemb, size_t size) {
  void* ptr;
  Increment(&num_malloc_calls);
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
  assert(nmemb * size > 0);
  ptr = malloc((size_t)(nmemb * size));
  AddMem(ptr, (size_t)(nmemb * size));
  return ptr;
}

I don't fully understand what that does, but it seems to be doing some additional checks and management. Not sure if it's necessary to use it, but might as well if the library recommends it?

After providing the data as a Pointer, I got the decoder object. The only thing from there that I had some issues with was the function to get the next frame:

WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
                                                   uint8_t** buf, int* timestamp);

It seems to be a double pointer? So I had to get the Pointer from the reference, and then the Pointer from that again:

PointerByReference buf = new PointerByReference();
IntByReference timestamp = new IntByReference();
int nextResult = LibWebPDemux.INSTANCE.WebPAnimDecoderGetNext(dec, buf, timestamp);
ImageIcon icon = createImage(buf.getPointer().getPointer(0), info.canvas_width, info.canvas_height);

Here's the updated code, cleaned up a bit to what is actually used. I'm not sure if it's entirely correct, but I'm getting the frames and other info now, so it shouldn't be that wrong at least.

It definitely needs some proper error checking and I'm not sure if I need to clean up any more memory afterwards.

package webptest;

import com.sun.jna.Library;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.BaseTSD;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Hashtable;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class WebPDecodingTest1 {

    public static void main(String[] args) throws Exception {
        System.setProperty("jna.library.path", "<path>\\lib\\x64");
        ImageIcon[] animatedImage = decodeAnimated("https://storage.googleapis.com/downloads.webmproject.org/webp/images/dancing_banana2.lossless.webp");
        showImages(animatedImage);
    }
    
    //==========================
    // Animated image
    //==========================
    
    /**
     * Decode the frames of an animated image. Doesn't work and incomplete.
     */
    private static ImageIcon[] decodeAnimated(String url) throws Exception {
        byte[] rawData = getBytesFromURL(new URL(url));
        LibWebPDemux.WebPData data = new LibWebPDemux.WebPData();
        data.bytes = LibWebP.INSTANCE.WebPMalloc(rawData.length);
        // This worked as well
//        data.bytes = new Memory(rawData.length);

        data.bytes.write(0, rawData, 0, rawData.length);
        data.length = rawData.length;
        Pointer dec = LibWebPDemux.INSTANCE.WebPAnimDecoderNewInternal(data, null, LibWebPDemux.WEBP_DEMUX_ABI_VERSION);
        System.out.println(dec);
        
        LibWebPDemux.WebPAnimInfo info = new LibWebPDemux.WebPAnimInfo();
        LibWebPDemux.INSTANCE.WebPAnimDecoderGetInfo(dec, info);
        System.out.println(info.canvas_width+" "+info.canvas_height+" "+info.frame_count);
        ImageIcon[] result = new ImageIcon[info.frame_count];
        int frame = 0;
        while (LibWebPDemux.INSTANCE.WebPAnimDecoderHasMoreFrames(dec) == 1) {
            PointerByReference buf = new PointerByReference();
            IntByReference timestamp = new IntByReference();
            
            int nextResult = LibWebPDemux.INSTANCE.WebPAnimDecoderGetNext(dec, buf, timestamp);
            System.out.println(nextResult+" "+timestamp.getValue());
            
            ImageIcon icon = createImage(buf.getPointer().getPointer(0), info.canvas_width, info.canvas_height);
            result[frame] = icon;
            frame++;
        }
        /**
         * Not sure if this is correct or if other stuff needs to be cleared as
         * well.
         */
        LibWebPDemux.INSTANCE.WebPAnimDecoderDelete(dec);
        LibWebP.INSTANCE.WebPFree(data.bytes);
        return result;
    }
    
    //==========================
    // libwebp
    //==========================
    public interface LibWebP extends Library {

        LibWebP INSTANCE = Native.load("libwebp", LibWebP.class);
        
        /*
        [webp/types.h]
            // Allocates 'size' bytes of memory. Returns NULL upon error. Memory
            // must be deallocated by calling WebPFree(). This function is made available
            // by the core 'libwebp' library.
            WEBP_EXTERN void* WebPMalloc(size_t size);
        */
        public Pointer WebPMalloc(int size);
        
        /*
        [webp/types.h]
            // Releases memory returned by the WebPDecode*() functions (from decode.h).
            WEBP_EXTERN void WebPFree(void* ptr);
        */
        public void WebPFree(Pointer pointer);
    }
    
    //==========================
    // libwebpdemux
    //==========================
    public interface LibWebPDemux extends Library {
        LibWebPDemux INSTANCE = Native.load("libwebpdemux", LibWebPDemux.class);
        
        static final int WEBP_DEMUX_ABI_VERSION = 0x0107;
        
        /*
        [webp/demux.h]
            // Internal, version-checked, entry point.
            WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
                const WebPData*, const WebPAnimDecoderOptions*, int);

            // Creates and initializes a WebPAnimDecoder object.
            // Parameters:
            //   webp_data - (in) WebP bitstream. This should remain unchanged during the
            //                    lifetime of the output WebPAnimDecoder object.
            //   dec_options - (in) decoding options. Can be passed NULL to choose
            //                      reasonable defaults (in particular, color mode MODE_RGBA
            //                      will be picked).
            // Returns:
            //   A pointer to the newly created WebPAnimDecoder object, or NULL in case of
            //   parsing error, invalid option or memory error.
            static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
                const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) {
              return WebPAnimDecoderNewInternal(webp_data, dec_options,
                                                WEBP_DEMUX_ABI_VERSION);
            }
        */
        public Pointer WebPAnimDecoderNewInternal(WebPData webp_data, Structure dec_options, int version);
        
        /*
        [webp/mux_types.h]
            // Data type used to describe 'raw' data, e.g., chunk data
            // (ICC profile, metadata) and WebP compressed image data.
            // 'bytes' memory must be allocated using WebPMalloc() and such.
            struct WebPData {
              const uint8_t* bytes;
              size_t size;
            };
        */
        @Structure.FieldOrder({ "bytes", "length" })
        public static class WebPData extends Structure {
            public Pointer bytes;
            
            public long length;
        }
        
        /*
        [webp/demux.h]
            // Get global information about the animation.
            // Parameters:
            //   dec - (in) decoder instance to get information from.
            //   info - (out) global information fetched from the animation.
            // Returns:
            //   True on success.
            WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec,
                                                   WebPAnimInfo* info);
        */
        public int WebPAnimDecoderGetInfo(Pointer dec, WebPAnimInfo info);
        
        /*
        [webp/demux.h]
            // Global information about the animation..
            struct WebPAnimInfo {
              uint32_t canvas_width;
              uint32_t canvas_height;
              uint32_t loop_count;
              uint32_t bgcolor;
              uint32_t frame_count;
              uint32_t pad[4];   // padding for later use
            };
        */
        @Structure.FieldOrder({ "canvas_width", "canvas_height", "loop_count", "bgcolor", "frame_count", "pad" })
        public static class WebPAnimInfo extends Structure {
            public int canvas_width;
            public int canvas_height;
            public int loop_count;
            public int bgcolor;
            public int frame_count;
            public int[] pad = new int[4];
        }
        
        /*
        [webp/demux.h]
            // Check if there are more frames left to decode.
            // Parameters:
            //   dec - (in) decoder instance to be checked.
            // Returns:
            //   True if 'dec' is not NULL and some frames are yet to be decoded.
            //   Otherwise, returns false.
            WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec);
        */
        public int WebPAnimDecoderHasMoreFrames(Pointer dec);
        
        /*
        [webp/demux.h]
            // Fetch the next frame from 'dec' based on options supplied to
            // WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size
            // 'canvas_width * 4 * canvas_height', and not just the frame sub-rectangle. The
            // returned buffer 'buf' is valid only until the next call to
            // WebPAnimDecoderGetNext(), WebPAnimDecoderReset() or WebPAnimDecoderDelete().
            // Parameters:
            //   dec - (in/out) decoder instance from which the next frame is to be fetched.
            //   buf - (out) decoded frame.
            //   timestamp - (out) timestamp of the frame in milliseconds.
            // Returns:
            //   False if any of the arguments are NULL, or if there is a parsing or
            //   decoding error, or if there are no more frames. Otherwise, returns true.
            WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
                                                   uint8_t** buf, int* timestamp);
        */
        public int WebPAnimDecoderGetNext(Pointer dec, PointerByReference buf, IntByReference timestamp);
        
        /*
        [webp/demux.h]
            // Deletes the WebPAnimDecoder object.
            // Parameters:
            //   dec - (in/out) decoder instance to be deleted
            WEBP_EXTERN void WebPAnimDecoderDelete(WebPAnimDecoder* dec);
        */
        public void WebPAnimDecoderDelete(Pointer dec);
    }
    
    //==========================
    // General Helpers
    //==========================
    private static ImageIcon createImage(Pointer pixelData, int width, int height) {
        if (pixelData != null) {
            int[] pixels = pixelData.getIntArray(0, width * height);

            ColorModel colorModel;
            colorModel = new DirectColorModel(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);

            SampleModel sampleModel = colorModel.createCompatibleSampleModel(width, height);
            DataBufferInt db = new DataBufferInt(pixels, width * height);
            WritableRaster raster = WritableRaster.createWritableRaster(sampleModel, db, null);

            Image img = new BufferedImage(colorModel, raster, false, new Hashtable<Object, Object>());
            return new ImageIcon(img.getScaledInstance(100, 100, 0)); // Resized for testing
        }
        return null;
    }
    
    private static void showImages(ImageIcon... icons) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            frame.setLayout(new FlowLayout());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            for (ImageIcon icon : icons) {
                frame.add(new JLabel(icon), BorderLayout.CENTER);
            }
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
        
    }
    
    public static byte[] getBytesFromURL(URL url) throws Exception {
        URLConnection c = url.openConnection();
        try (InputStream input = c.getInputStream()) {
            byte[] imageData = readAllBytes(input);
            return imageData;
        }
    }
    
    private static byte[] readAllBytes(InputStream input) throws IOException {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = input.read(buffer, 0, buffer.length)) != -1) {
            result.write(buffer, 0, length);
        }
        return result.toByteArray();
    }
    
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文