diff --git a/README.md b/README.md index 5740a6b..0624b17 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ For easier to use, qwong/j-webp (bases on [webp project of Luciad](https://bitbu However, coders pefer using jar package instead of source java code in personal project. So I fork and edit qwong/j-webp to privide a usable jar. It is a fat jar includes dependencies and native lib file (includes windows/linux/mac both 32&64bit). +Update 20181119: sync from [webp project of Luciad](https://bitbucket.org/luciad/webp-imageio) 1.0.0 + ## Usage Because it is not in maven repo, so you have to put the jar file `webp-imageio-core-{version}.jar` into libs folder of your project manually. diff --git a/src/main/java/com/luciad/imageio/webp/WebP.java b/src/main/java/com/luciad/imageio/webp/WebP.java index cdda853..c8dbe1e 100644 --- a/src/main/java/com/luciad/imageio/webp/WebP.java +++ b/src/main/java/com/luciad/imageio/webp/WebP.java @@ -17,16 +17,14 @@ package com.luciad.imageio.webp; import org.scijava.nativelib.NativeLibraryUtil; -import java.awt.image.*; import java.io.IOException; import java.nio.ByteOrder; -import java.util.Hashtable; final class WebP { private static boolean NATIVE_LIBRARY_LOADED = false; static synchronized void loadNativeLibrary() { - if ( !NATIVE_LIBRARY_LOADED ) { + if (!NATIVE_LIBRARY_LOADED) { NATIVE_LIBRARY_LOADED = true; NativeLibraryUtil.loadNativeLibrary(WebP.class, "webp-imageio"); } @@ -39,390 +37,56 @@ final class WebP { private WebP() { } - public static BufferedImage decode( WebPReadParam aReadParam, byte[] aData, int aOffset, int aLength ) throws IOException { - if ( aReadParam == null ) { - throw new NullPointerException( "Decoder options may not be null" ); + public static int[] decode(WebPDecoderOptions aOptions, byte[] aData, int aOffset, int aLength, int[] aOut) throws IOException { + if (aOptions == null) { + throw new NullPointerException("Decoder options may not be null"); } - if ( aData == null ) { - throw new NullPointerException( "Input data may not be null" ); + if (aData == null) { + throw new NullPointerException("Input data may not be null"); } - if ( aOffset + aLength > aData.length ) { - throw new IllegalArgumentException( "Offset/length exceeds array size" ); + if (aOffset + aLength > aData.length) { + throw new IllegalArgumentException("Offset/length exceeds array size"); } - int[] out = new int[4]; - int[] pixels = decode( aReadParam.fPointer, aData, aOffset, aLength, out, ByteOrder.nativeOrder().equals( ByteOrder.BIG_ENDIAN ) ); - VP8StatusCode status = VP8StatusCode.getStatusCode( out[0] ); - switch ( status ) { - case VP8_STATUS_OK: - break; - case VP8_STATUS_OUT_OF_MEMORY: - throw new OutOfMemoryError(); - default: - throw new IOException( "Decode returned code " + status ); + int[] pixels = decode(aOptions.fPointer, aData, aOffset, aLength, aOut, ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)); + VP8StatusCode status = VP8StatusCode.getStatusCode(aOut[0]); + switch (status) { + case VP8_STATUS_OK: + break; + case VP8_STATUS_OUT_OF_MEMORY: + throw new OutOfMemoryError(); + default: + throw new IOException("Decode returned code " + status); } - int width = out[1]; - int height = out[2]; - boolean alpha = out[3] != 0; - - ColorModel colorModel; - if ( alpha ) { - colorModel = new DirectColorModel( 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 ); - } else { - colorModel = new DirectColorModel( 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 ); - } - - SampleModel sampleModel = colorModel.createCompatibleSampleModel( width, height ); - DataBufferInt db = new DataBufferInt( pixels, width * height ); - WritableRaster raster = WritableRaster.createWritableRaster( sampleModel, db, null ); - - return new BufferedImage( colorModel, raster, false, new Hashtable() ); + return pixels; } - private static native int[] decode( long aDecoderOptionsPointer, byte[] aData, int aOffset, int aLength, int[] aFlags, boolean aBigEndian ); + private static native int[] decode(long aDecoderOptionsPointer, byte[] aData, int aOffset, int aLength, int[] aFlags, boolean aBigEndian); - public static int[] getInfo( byte[] aData, int aOffset, int aLength ) throws IOException { + public static int[] getInfo(byte[] aData, int aOffset, int aLength) throws IOException { int[] out = new int[2]; - int result = getInfo( aData, aOffset, aLength, out ); + int result = getInfo(aData, aOffset, aLength, out); if (result == 0) { - throw new IOException( "Invalid WebP data" ); + throw new IOException("Invalid WebP data"); } return out; } - private static native int getInfo( byte[] aData, int aOffset, int aLength, int[] aOut ); + private static native int getInfo(byte[] aData, int aOffset, int aLength, int[] aOut); - public static byte[] encode( WebPWriteParam aWriteParam, RenderedImage aImage ) throws IOException { - if ( aWriteParam == null ) { - throw new NullPointerException( "Encoder options may not be null" ); - } - - if ( aImage == null ) { - throw new NullPointerException( "Image may not be null" ); - } - - boolean encodeAlpha = hasTranslucency( aImage ); - if ( encodeAlpha ) { - byte[] rgbaData = getRGBA( aImage ); - return encodeRGBA( aWriteParam.getPointer(), rgbaData, aImage.getWidth(), aImage.getHeight(), aImage.getWidth() * 4 ); - } - else { - byte[] rgbData = getRGB( aImage ); - return encodeRGB( aWriteParam.getPointer(), rgbData, aImage.getWidth(), aImage.getHeight(), aImage.getWidth() * 3 ); - } + public static byte[] encodeRGBA(WebPEncoderOptions aOptions, byte[] aRgbaData, int aWidth, int aHeight, int aStride) { + return encodeRGBA(aOptions.fPointer, aRgbaData, aWidth, aHeight, aStride); } - private static native byte[] encodeRGBA( long aConfig, byte[] aRgbaData, int aWidth, int aHeight, int aStride ); + private static native byte[] encodeRGBA(long aConfig, byte[] aRgbaData, int aWidth, int aHeight, int aStride); - private static native byte[] encodeRGB( long aConfig, byte[] aRgbaData, int aWidth, int aHeight, int aStride ); - - private static boolean hasTranslucency( RenderedImage aRi ) { - return aRi.getColorModel().hasAlpha(); + public static byte[] encodeRGB(WebPEncoderOptions aOptions, byte[] aRgbaData, int aWidth, int aHeight, int aStride) { + return encodeRGB(aOptions.fPointer, aRgbaData, aWidth, aHeight, aStride); } - private static int getShift( int aMask ) { - int shift = 0; - while ( ( ( aMask >> shift ) & 0x1 ) == 0 ) { - shift++; - } - return shift; - } - - private static byte[] getRGB( RenderedImage aRi ) throws IOException { - int width = aRi.getWidth(); - int height = aRi.getHeight(); - - ColorModel colorModel = aRi.getColorModel(); - if ( colorModel instanceof ComponentColorModel ) { - ComponentSampleModel sampleModel = ( ComponentSampleModel ) aRi.getSampleModel(); - int type = sampleModel.getTransferType(); - if ( type == DataBuffer.TYPE_BYTE ) { - return extractComponentRGBByte( width, height, sampleModel, ( ( DataBufferByte ) aRi.getData().getDataBuffer() ) ); - } - else if ( type == DataBuffer.TYPE_INT ) { - return extractComponentRGBInt( width, height, sampleModel, ( ( DataBufferInt ) aRi.getData().getDataBuffer() ) ); - } - else { - throw new IOException( "Incompatible image: " + aRi ); - } - } - else if ( colorModel instanceof DirectColorModel ) { - SinglePixelPackedSampleModel sampleModel = ( SinglePixelPackedSampleModel ) aRi.getSampleModel(); - int type = sampleModel.getTransferType(); - if ( type == DataBuffer.TYPE_INT ) { - return extractDirectRGBInt( width, height, ( DirectColorModel ) colorModel, sampleModel, ( ( DataBufferInt ) aRi.getData().getDataBuffer() ) ); - } - else { - throw new IOException( "Incompatible image: " + aRi ); - } - } - - return extractGenericRGB( aRi, width, height, colorModel ); - } - - private static byte[] extractGenericRGB( RenderedImage aRi, int aWidth, int aHeight, ColorModel aColorModel ) { - Object dataElements = null; - byte[] rgbData = new byte[ aWidth * aHeight * 3 ]; - for ( int b = 0, y = 0; y < aHeight; y++ ) { - for ( int x = 0; x < aWidth; x++, b += 3 ) { - dataElements = aRi.getData().getDataElements( x, y, dataElements ); - rgbData[ b ] = ( byte ) aColorModel.getRed( dataElements ); - rgbData[ b + 1 ] = ( byte ) aColorModel.getGreen( dataElements ); - rgbData[ b + 2 ] = ( byte ) aColorModel.getBlue( dataElements ); - } - } - return rgbData; - } - - private static byte[] extractDirectRGBInt( int aWidth, int aHeight, DirectColorModel aColorModel, SinglePixelPackedSampleModel aSampleModel, DataBufferInt aDataBuffer ) { - byte[] out = new byte[ aWidth * aHeight * 3 ]; - - int rMask = aColorModel.getRedMask(); - int gMask = aColorModel.getGreenMask(); - int bMask = aColorModel.getBlueMask(); - int rShift = getShift( rMask ); - int gShift = getShift( gMask ); - int bShift = getShift( bMask ); - int[] bank = aDataBuffer.getBankData()[ 0 ]; - int scanlineStride = aSampleModel.getScanlineStride(); - int scanIx = 0; - for ( int b = 0, y = 0; y < aHeight; y++ ) { - int pixIx = scanIx; - for ( int x = 0; x < aWidth; x++, b += 3 ) { - int pixel = bank[ pixIx++ ]; - out[ b ] = ( byte ) ( ( pixel & rMask ) >>> rShift ); - out[ b + 1 ] = ( byte ) ( ( pixel & gMask ) >>> gShift ); - out[ b + 2 ] = ( byte ) ( ( pixel & bMask ) >>> bShift ); - } - scanIx += scanlineStride; - } - return out; - } - - private static byte[] extractComponentRGBInt( int aWidth, int aHeight, ComponentSampleModel aSampleModel, DataBufferInt aDataBuffer ) { - byte[] out = new byte[ aWidth * aHeight * 3 ]; - - int[] bankIndices = aSampleModel.getBankIndices(); - int[] rBank = aDataBuffer.getBankData()[ bankIndices[ 0 ] ]; - int[] gBank = aDataBuffer.getBankData()[ bankIndices[ 1 ] ]; - int[] bBank = aDataBuffer.getBankData()[ bankIndices[ 2 ] ]; - - int[] bankOffsets = aSampleModel.getBandOffsets(); - int rScanIx = bankOffsets[ 0 ]; - int gScanIx = bankOffsets[ 1 ]; - int bScanIx = bankOffsets[ 2 ]; - - int pixelStride = aSampleModel.getPixelStride(); - int scanlineStride = aSampleModel.getScanlineStride(); - for ( int b = 0, y = 0; y < aHeight; y++ ) { - int rPixIx = rScanIx; - int gPixIx = gScanIx; - int bPixIx = bScanIx; - for ( int x = 0; x < aWidth; x++, b += 3 ) { - out[ b ] = ( byte ) rBank[ rPixIx ]; - rPixIx += pixelStride; - out[ b + 1 ] = ( byte ) gBank[ gPixIx ]; - gPixIx += pixelStride; - out[ b + 2 ] = ( byte ) bBank[ bPixIx ]; - bPixIx += pixelStride; - } - rScanIx += scanlineStride; - gScanIx += scanlineStride; - bScanIx += scanlineStride; - } - return out; - } - - private static byte[] extractComponentRGBByte( int aWidth, int aHeight, ComponentSampleModel aSampleModel, DataBufferByte aDataBuffer ) { - byte[] out = new byte[ aWidth * aHeight * 3 ]; - - int[] bankIndices = aSampleModel.getBankIndices(); - byte[] rBank = aDataBuffer.getBankData()[ bankIndices[ 0 ] ]; - byte[] gBank = aDataBuffer.getBankData()[ bankIndices[ 1 ] ]; - byte[] bBank = aDataBuffer.getBankData()[ bankIndices[ 2 ] ]; - - int[] bankOffsets = aSampleModel.getBandOffsets(); - int rScanIx = bankOffsets[ 0 ]; - int gScanIx = bankOffsets[ 1 ]; - int bScanIx = bankOffsets[ 2 ]; - - int pixelStride = aSampleModel.getPixelStride(); - int scanlineStride = aSampleModel.getScanlineStride(); - for ( int b = 0, y = 0; y < aHeight; y++ ) { - int rPixIx = rScanIx; - int gPixIx = gScanIx; - int bPixIx = bScanIx; - for ( int x = 0; x < aWidth; x++, b += 3 ) { - out[ b ] = rBank[ rPixIx ]; - rPixIx += pixelStride; - out[ b + 1 ] = gBank[ gPixIx ]; - gPixIx += pixelStride; - out[ b + 2 ] = bBank[ bPixIx ]; - bPixIx += pixelStride; - } - rScanIx += scanlineStride; - gScanIx += scanlineStride; - bScanIx += scanlineStride; - } - return out; - } - - private static byte[] getRGBA( RenderedImage aRi ) throws IOException { - int width = aRi.getWidth(); - int height = aRi.getHeight(); - - ColorModel colorModel = aRi.getColorModel(); - if ( colorModel instanceof ComponentColorModel ) { - ComponentSampleModel sampleModel = ( ComponentSampleModel ) aRi.getSampleModel(); - int type = sampleModel.getTransferType(); - if ( type == DataBuffer.TYPE_BYTE ) { - return extractComponentRGBAByte( width, height, sampleModel, ( ( DataBufferByte ) aRi.getData().getDataBuffer() ) ); - } - else if ( type == DataBuffer.TYPE_INT ) { - return extractComponentRGBAInt( width, height, sampleModel, ( ( DataBufferInt ) aRi.getData().getDataBuffer() ) ); - } - else { - throw new IOException( "Incompatible image: " + aRi ); - } - } - else if ( colorModel instanceof DirectColorModel ) { - SinglePixelPackedSampleModel sampleModel = ( SinglePixelPackedSampleModel ) aRi.getSampleModel(); - int type = sampleModel.getTransferType(); - if ( type == DataBuffer.TYPE_INT ) { - return extractDirectRGBAInt( width, height, ( DirectColorModel ) colorModel, sampleModel, ( ( DataBufferInt ) aRi.getData().getDataBuffer() ) ); - } - else { - throw new IOException( "Incompatible image: " + aRi ); - } - } - - return extractGenericRGBA( aRi, width, height, colorModel ); - } - - private static byte[] extractGenericRGBA( RenderedImage aRi, int aWidth, int aHeight, ColorModel aColorModel ) { - Object dataElements = null; - byte[] rgbData = new byte[ aWidth * aHeight * 4 ]; - for ( int b = 0, y = 0; y < aHeight; y++ ) { - for ( int x = 0; x < aWidth; x++, b += 4 ) { - dataElements = aRi.getData().getDataElements( x, y, dataElements ); - rgbData[ b ] = ( byte ) aColorModel.getRed( dataElements ); - rgbData[ b + 1 ] = ( byte ) aColorModel.getGreen( dataElements ); - rgbData[ b + 2 ] = ( byte ) aColorModel.getBlue( dataElements ); - rgbData[ b + 3 ] = ( byte ) aColorModel.getAlpha( dataElements ); - } - } - return rgbData; - } - - private static byte[] extractDirectRGBAInt( int aWidth, int aHeight, DirectColorModel aColorModel, SinglePixelPackedSampleModel aSampleModel, DataBufferInt aDataBuffer ) { - byte[] out = new byte[ aWidth * aHeight * 4 ]; - - int rMask = aColorModel.getRedMask(); - int gMask = aColorModel.getGreenMask(); - int bMask = aColorModel.getBlueMask(); - int aMask = aColorModel.getAlphaMask(); - int rShift = getShift( rMask ); - int gShift = getShift( gMask ); - int bShift = getShift( bMask ); - int aShift = getShift( aMask ); - int[] bank = aDataBuffer.getBankData()[ 0 ]; - int scanlineStride = aSampleModel.getScanlineStride(); - int scanIx = 0; - for ( int b = 0, y = 0; y < aHeight; y++ ) { - int pixIx = scanIx; - for ( int x = 0; x < aWidth; x++, b += 4 ) { - int pixel = bank[ pixIx++ ]; - out[ b ] = ( byte ) ( ( pixel & rMask ) >>> rShift ); - out[ b + 1 ] = ( byte ) ( ( pixel & gMask ) >>> gShift ); - out[ b + 2 ] = ( byte ) ( ( pixel & bMask ) >>> bShift ); - out[ b + 3 ] = ( byte ) ( ( pixel & aMask ) >>> aShift ); - } - scanIx += scanlineStride; - } - return out; - } - - private static byte[] extractComponentRGBAInt( int aWidth, int aHeight, ComponentSampleModel aSampleModel, DataBufferInt aDataBuffer ) { - byte[] out = new byte[ aWidth * aHeight * 4 ]; - - int[] bankIndices = aSampleModel.getBankIndices(); - int[] rBank = aDataBuffer.getBankData()[ bankIndices[ 0 ] ]; - int[] gBank = aDataBuffer.getBankData()[ bankIndices[ 1 ] ]; - int[] bBank = aDataBuffer.getBankData()[ bankIndices[ 2 ] ]; - int[] aBank = aDataBuffer.getBankData()[ bankIndices[ 3 ] ]; - - int[] bankOffsets = aSampleModel.getBandOffsets(); - int rScanIx = bankOffsets[ 0 ]; - int gScanIx = bankOffsets[ 1 ]; - int bScanIx = bankOffsets[ 2 ]; - int aScanIx = bankOffsets[ 3 ]; - - int pixelStride = aSampleModel.getPixelStride(); - int scanlineStride = aSampleModel.getScanlineStride(); - for ( int b = 0, y = 0; y < aHeight; y++ ) { - int rPixIx = rScanIx; - int gPixIx = gScanIx; - int bPixIx = bScanIx; - int aPixIx = aScanIx; - for ( int x = 0; x < aWidth; x++, b += 4 ) { - out[ b ] = ( byte ) rBank[ rPixIx ]; - rPixIx += pixelStride; - out[ b + 1 ] = ( byte ) gBank[ gPixIx ]; - gPixIx += pixelStride; - out[ b + 2 ] = ( byte ) bBank[ bPixIx ]; - bPixIx += pixelStride; - out[ b + 3 ] = ( byte ) aBank[ aPixIx ]; - aPixIx += pixelStride; - } - rScanIx += scanlineStride; - gScanIx += scanlineStride; - bScanIx += scanlineStride; - aScanIx += scanlineStride; - } - return out; - } - - private static byte[] extractComponentRGBAByte( int aWidth, int aHeight, ComponentSampleModel aSampleModel, DataBufferByte aDataBuffer ) { - byte[] out = new byte[ aWidth * aHeight * 4 ]; - - int[] bankIndices = aSampleModel.getBankIndices(); - byte[] rBank = aDataBuffer.getBankData()[ bankIndices[ 0 ] ]; - byte[] gBank = aDataBuffer.getBankData()[ bankIndices[ 1 ] ]; - byte[] bBank = aDataBuffer.getBankData()[ bankIndices[ 2 ] ]; - byte[] aBank = aDataBuffer.getBankData()[ bankIndices[ 3 ] ]; - - int[] bankOffsets = aSampleModel.getBandOffsets(); - int rScanIx = bankOffsets[ 0 ]; - int gScanIx = bankOffsets[ 1 ]; - int bScanIx = bankOffsets[ 2 ]; - int aScanIx = bankOffsets[ 3 ]; - - int pixelStride = aSampleModel.getPixelStride(); - int scanlineStride = aSampleModel.getScanlineStride(); - for ( int b = 0, y = 0; y < aHeight; y++ ) { - int rPixIx = rScanIx; - int gPixIx = gScanIx; - int bPixIx = bScanIx; - int aPixIx = aScanIx; - for ( int x = 0; x < aWidth; x++, b += 4 ) { - out[ b ] = rBank[ rPixIx ]; - rPixIx += pixelStride; - out[ b + 1 ] = gBank[ gPixIx ]; - gPixIx += pixelStride; - out[ b + 2 ] = bBank[ bPixIx ]; - bPixIx += pixelStride; - out[ b + 3 ] = aBank[ aPixIx ]; - aPixIx += pixelStride; - } - rScanIx += scanlineStride; - gScanIx += scanlineStride; - bScanIx += scanlineStride; - aScanIx += scanlineStride; - } - return out; - } + private static native byte[] encodeRGB(long aConfig, byte[] aRgbaData, int aWidth, int aHeight, int aStride); } diff --git a/src/main/java/com/luciad/imageio/webp/WebPDecoderOptions.java b/src/main/java/com/luciad/imageio/webp/WebPDecoderOptions.java new file mode 100644 index 0000000..93153bb --- /dev/null +++ b/src/main/java/com/luciad/imageio/webp/WebPDecoderOptions.java @@ -0,0 +1,182 @@ +/* + * Copyright 2013 Luciad (http://www.luciad.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.luciad.imageio.webp; + +public final class WebPDecoderOptions { + static { + WebP.loadNativeLibrary(); + } + + long fPointer; + + public WebPDecoderOptions() { + fPointer = createDecoderOptions(); + if ( fPointer == 0 ) { + throw new OutOfMemoryError(); + } + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + deleteDecoderOptions( fPointer ); + fPointer = 0L; + } + + public int getCropHeight() { + return getCropHeight( fPointer ); + } + + public void setCropHeight( int aCropHeight ) { + setCropHeight( fPointer, aCropHeight ); + } + + public int getCropLeft() { + return getCropLeft( fPointer ); + } + + public void setCropLeft( int aCropLeft ) { + setCropLeft( fPointer, aCropLeft ); + } + + public int getCropTop() { + return getCropTop( fPointer ); + } + + public void setCropTop( int aCropTop ) { + setCropTop( fPointer, aCropTop ); + } + + public int getCropWidth() { + return getCropWidth( fPointer ); + } + + public void setCropWidth( int aCropWidth ) { + setCropWidth( fPointer, aCropWidth ); + } + + public boolean isFancyUpsampling() { + return !isNoFancyUpsampling( fPointer ); + } + + public void setFancyUpsampling( boolean aFancyUpsampling ) { + setNoFancyUpsampling( fPointer, !aFancyUpsampling ); + } + + public int getScaledHeight() { + return getScaledHeight( fPointer ); + } + + public void setScaledHeight( int aScaledHeight ) { + setScaledHeight( fPointer, aScaledHeight ); + } + + public int getScaledWidth() { + return getScaledWidth( fPointer ); + } + + public void setScaledWidth( int aScaledWidth ) { + setScaledWidth( fPointer, aScaledWidth ); + } + + public boolean isUseCropping() { + return isUseCropping( fPointer ); + } + + public void setUseCropping( boolean aUseCropping ) { + setUseCropping( fPointer, aUseCropping ); + } + + public boolean isUseScaling() { + return isUseScaling( fPointer ); + } + + public void setUseScaling( boolean aUseScaling ) { + setUseScaling( fPointer, aUseScaling ); + } + + public boolean isUseThreads() { + return isUseThreads( fPointer ); + } + + public void setUseThreads( boolean aUseThreads ) { + setUseThreads( fPointer, aUseThreads ); + } + + public boolean isBypassFiltering() { + return isBypassFiltering( fPointer ); + } + + public void setBypassFiltering( boolean aBypassFiltering ) { + setBypassFiltering( fPointer, aBypassFiltering ); + } + + private static native long createDecoderOptions(); + + private static native void deleteDecoderOptions( long aPointer ); + + private static native int getCropHeight( long aPointer ); + + private static native void setCropHeight( long aPointer, int aCropHeight ); + + private static native int getCropLeft( long aPointer ); + + private static native void setCropLeft( long aPointer, int aCropLeft ); + + private static native int getCropTop( long aPointer ); + + private static native void setCropTop( long aPointer, int aCropTop ); + + private static native int getCropWidth( long aPointer ); + + private static native void setCropWidth( long aPointer, int aCropWidth ); + + private static native boolean isForceRotation( long aPointer ); + + private static native void setForceRotation( long aPointer, boolean aForceRotation ); + + private static native boolean isNoEnhancement( long aPointer ); + + private static native void setNoEnhancement( long aPointer, boolean aNoEnhancement ); + + private static native boolean isNoFancyUpsampling( long aPointer ); + + private static native void setNoFancyUpsampling( long aPointer, boolean aFancyUpsampling ); + + private static native int getScaledHeight( long aPointer ); + + private static native void setScaledHeight( long aPointer, int aScaledHeight ); + + private static native int getScaledWidth( long aPointer ); + + private static native void setScaledWidth( long aPointer, int aScaledWidth ); + + private static native boolean isUseCropping( long aPointer ); + + private static native void setUseCropping( long aPointer, boolean aUseCropping ); + + private static native boolean isUseScaling( long aPointer ); + + private static native void setUseScaling( long aPointer, boolean aUseScaling ); + + private static native boolean isUseThreads( long aPointer ); + + private static native void setUseThreads( long aPointer, boolean aUseThreads ); + + private static native boolean isBypassFiltering( long aPointer ); + + private static native void setBypassFiltering( long aPointer, boolean aBypassFiltering ); +} diff --git a/src/main/java/com/luciad/imageio/webp/WebPEncoderOptions.java b/src/main/java/com/luciad/imageio/webp/WebPEncoderOptions.java new file mode 100644 index 0000000..9d83f63 --- /dev/null +++ b/src/main/java/com/luciad/imageio/webp/WebPEncoderOptions.java @@ -0,0 +1,310 @@ +/* + * Copyright 2013 Luciad (http://www.luciad.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.luciad.imageio.webp; + +public class WebPEncoderOptions { + static { + WebP.loadNativeLibrary(); + } + + long fPointer; + + public WebPEncoderOptions() { + fPointer = createConfig(); + if ( fPointer == 0 ) { + throw new OutOfMemoryError(); + } + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + deleteConfig( fPointer ); + fPointer = 0L; + } + + private static native long createConfig(); + + private static native void deleteConfig( long aPointer ); + + long getPointer() { + return fPointer; + } + + public float getCompressionQuality() { + return getQuality(fPointer); + } + + public void setCompressionQuality( float quality ) { + setQuality( fPointer, quality ); + } + + public boolean isLossless() { + return getLossless( fPointer ) != 0; + } + + public void setLossless( boolean aLossless ) { + setLossless(fPointer, aLossless ? 1 : 0); + } + + public int getTargetSize() { + return getTargetSize( fPointer ); + } + + public void setTargetSize( int aTargetSize ) { + setTargetSize( fPointer, aTargetSize ); + } + + public float getTargetPSNR() { + return getTargetPSNR( fPointer ); + } + + public void setTargetPSNR( float aTargetPSNR ) { + setTargetPSNR( fPointer, aTargetPSNR ); + } + + public int getMethod() { + return getMethod( fPointer ); + } + + public void setMethod( int aMethod ) { + setMethod( fPointer, aMethod ); + } + + public int getSegments() { + return getSegments( fPointer ); + } + + public void setSegments( int aSegments ) { + setSegments( fPointer, aSegments ); + } + + public int getSnsStrength() { + return getSnsStrength( fPointer ); + } + + public void setSnsStrength( int aSnsStrength ) { + setSnsStrength( fPointer, aSnsStrength ); + } + + public int getFilterStrength() { + return getFilterStrength( fPointer ); + } + + public void setFilterStrength( int aFilterStrength ) { + setFilterStrength( fPointer, aFilterStrength ); + } + + public int getFilterSharpness() { + return getFilterSharpness( fPointer ); + } + + public void setFilterSharpness( int aFilterSharpness ) { + setFilterSharpness( fPointer, aFilterSharpness ); + } + + public int getFilterType() { + return getFilterType( fPointer ); + } + + public void setFilterType( int aFilterType ) { + setFilterType( fPointer, aFilterType ); + } + + public boolean isAutoAdjustFilterStrength() { + return getAutofilter( fPointer ) != 0; + } + + public void setAutoAdjustFilterStrength( boolean aAutofilter ) { + setAutofilter( fPointer, aAutofilter ? 1 : 0 ); + } + + public int getEntropyAnalysisPassCount() { + return getPass( fPointer ); + } + + public void setEntropyAnalysisPassCount( int aPass ) { + setPass( fPointer, aPass ); + } + + public boolean isShowCompressed() { + return getShowCompressed( fPointer ) != 0; + } + + public void setShowCompressed( boolean aShowCompressed ) { + setShowCompressed( fPointer, aShowCompressed ? 1 : 0 ); + } + + public int getPreprocessing() { + return getPreprocessing( fPointer ); + } + + public void setPreprocessing( int aPreprocessing ) { + setPreprocessing( fPointer, aPreprocessing ); + } + + public int getPartitions() { + return getPartitions( fPointer ); + } + + public void setPartitions( int aPartitions ) { + setPartitions( fPointer, aPartitions ); + } + + public int getPartitionLimit() { + return getPartitionLimit( fPointer ); + } + + public void setPartitionLimit( int aPartitionLimit ) { + setPartitionLimit( fPointer, aPartitionLimit ); + } + + public int getAlphaCompression() { + return getAlphaCompression( fPointer ); + } + + public void setAlphaCompression( int aAlphaCompression ) { + setAlphaCompression( fPointer, aAlphaCompression ); + } + + public int getAlphaFiltering() { + return getAlphaFiltering( fPointer ); + } + + public void setAlphaFiltering( int aAlphaFiltering ) { + setAlphaFiltering( fPointer, aAlphaFiltering ); + } + + public int getAlphaQuality() { + return getAlphaQuality( fPointer ); + } + + public void setAlphaQuality( int aAlphaQuality ) { + setAlphaQuality( fPointer, aAlphaQuality ); + } + + public boolean isEmulateJpegSize() { + return getEmulateJpegSize( fPointer ) != 0; + } + + public void setEmulateJpegSize( boolean aEmulateJpegSize ) { + setEmulateJpegSize( fPointer, aEmulateJpegSize ? 1 : 0 ); + } + + public int getThreadLevel() { + return getThreadLevel( fPointer ); + } + + public void setThreadLevel( int aThreadLevel ) { + setThreadLevel( fPointer, aThreadLevel ); + } + + public boolean isReduceMemoryUsage() { + return getLowMemory( fPointer ) != 0; + } + + public void setReduceMemoryUsage( boolean aLowMemory ) { + setLowMemory( fPointer, aLowMemory ? 1 : 0 ); + } + + private static native float getQuality( long aPointer ); + + private static native void setQuality( long aPointer, float aQuality ); + + private static native int getTargetSize( long aPointer ); + + private static native void setTargetSize( long aPointer, int aTargetSize ); + + private static native float getTargetPSNR( long aPointer ); + + private static native void setTargetPSNR( long aPointer, float aTargetPSNR ); + + private static native int getMethod( long aPointer ); + + private static native void setMethod( long aPointer, int aMethod ); + + private static native int getSegments( long aPointer ); + + private static native void setSegments( long aPointer, int aSegments ); + + private static native int getSnsStrength( long aPointer ); + + private static native void setSnsStrength( long aPointer, int aSnsStrength ); + + private static native int getFilterStrength( long aPointer ); + + private static native void setFilterStrength( long aPointer, int aFilterStrength ); + + private static native int getFilterSharpness( long aPointer ); + + private static native void setFilterSharpness( long aPointer, int aFilterSharpness ); + + private static native int getFilterType( long aPointer ); + + private static native void setFilterType( long aPointer, int aFilterType ); + + private static native int getAutofilter( long aPointer ); + + private static native void setAutofilter( long aPointer, int aAutofilter ); + + private static native int getPass( long aPointer ); + + private static native void setPass( long aPointer, int aPass ); + + private static native int getShowCompressed( long aPointer ); + + private static native void setShowCompressed( long aPointer, int aShowCompressed ); + + private static native int getPreprocessing( long aPointer ); + + private static native void setPreprocessing( long aPointer, int aPreprocessing ); + + private static native int getPartitions( long aPointer ); + + private static native void setPartitions( long aPointer, int aPartitions ); + + private static native int getPartitionLimit( long aPointer ); + + private static native void setPartitionLimit( long aPointer, int aPartitionLimit ); + + private static native int getAlphaCompression( long aPointer ); + + private static native void setAlphaCompression( long aPointer, int aAlphaCompression ); + + private static native int getAlphaFiltering( long aPointer ); + + private static native void setAlphaFiltering( long aPointer, int aAlphaFiltering ); + + private static native int getAlphaQuality( long aPointer ); + + private static native void setAlphaQuality( long aPointer, int aAlphaQuality ); + + private static native int getLossless( long aPointer ); + + private static native void setLossless( long aPointer, int aLossless ); + + private static native int getEmulateJpegSize( long aPointer ); + + private static native void setEmulateJpegSize( long aPointer, int aEmulateJpegSize ); + + private static native int getThreadLevel( long aPointer ); + + private static native void setThreadLevel( long aPointer, int aThreadLevel ); + + private static native int getLowMemory( long aPointer ); + + private static native void setLowMemory( long aPointer, int aLowMemory ); +} diff --git a/src/main/java/com/luciad/imageio/webp/WebPImageReaderSpi.java b/src/main/java/com/luciad/imageio/webp/WebPImageReaderSpi.java index dc539da..bf4a37b 100644 --- a/src/main/java/com/luciad/imageio/webp/WebPImageReaderSpi.java +++ b/src/main/java/com/luciad/imageio/webp/WebPImageReaderSpi.java @@ -30,6 +30,7 @@ public class WebPImageReaderSpi extends ImageReaderSpi { private static final byte[] RIFF = new byte[]{ 'R', 'I', 'F', 'F' }; private static final byte[] WEBP = new byte[]{ 'W', 'E', 'B', 'P' }; private static final byte[] VP8_ = new byte[]{ 'V', 'P', '8', ' ' }; + private static final byte[] VP8L = new byte[]{ 'V', 'P', '8', 'L' }; private static final byte[] VP8X = new byte[]{ 'V', 'P', '8', 'X' }; public WebPImageReaderSpi() { @@ -88,7 +89,7 @@ public class WebPImageReaderSpi extends ImageReaderSpi { } stream.readFully( b ); - if ( !Arrays.equals( b, VP8_ ) && !Arrays.equals( b, VP8X ) ) { + if ( !Arrays.equals( b, VP8_ ) && !Arrays.equals( b, VP8L ) && !Arrays.equals( b, VP8X ) ) { return false; } } finally { diff --git a/src/main/java/com/luciad/imageio/webp/WebPReadParam.java b/src/main/java/com/luciad/imageio/webp/WebPReadParam.java index 2db6712..52bf241 100644 --- a/src/main/java/com/luciad/imageio/webp/WebPReadParam.java +++ b/src/main/java/com/luciad/imageio/webp/WebPReadParam.java @@ -18,183 +18,101 @@ package com.luciad.imageio.webp; import javax.imageio.ImageReadParam; public final class WebPReadParam extends ImageReadParam { - static { - WebP.loadNativeLibrary(); - } - - long fPointer; + private WebPDecoderOptions fOptions; public WebPReadParam() { - fPointer = createDecoderOptions(); - if ( fPointer == 0 ) { - throw new OutOfMemoryError(); - } + fOptions = new WebPDecoderOptions(); } - @Override - protected void finalize() throws Throwable { - super.finalize(); - deleteDecoderOptions( fPointer ); - fPointer = 0L; + public void setScaledHeight(int aScaledHeight) { + fOptions.setScaledHeight(aScaledHeight); + } + + public void setUseScaling(boolean aUseScaling) { + fOptions.setUseScaling(aUseScaling); + } + + public void setUseThreads(boolean aUseThreads) { + fOptions.setUseThreads(aUseThreads); } public int getCropHeight() { - return getCropHeight( fPointer ); - } - - public void setCropHeight( int aCropHeight ) { - setCropHeight( fPointer, aCropHeight ); - } - - public int getCropLeft() { - return getCropLeft( fPointer ); - } - - public void setCropLeft( int aCropLeft ) { - setCropLeft( fPointer, aCropLeft ); - } - - public int getCropTop() { - return getCropTop( fPointer ); - } - - public void setCropTop( int aCropTop ) { - setCropTop( fPointer, aCropTop ); - } - - public int getCropWidth() { - return getCropWidth( fPointer ); - } - - public void setCropWidth( int aCropWidth ) { - setCropWidth( fPointer, aCropWidth ); - } - - public boolean isForceRotation() { - return isForceRotation( fPointer ); - } - - public void setForceRotation( boolean aForceRotation ) { - setForceRotation( fPointer, aForceRotation ); - } - - public boolean isEnhancement() { - return !isNoEnhancement( fPointer ); - } - - public void setEnhancement( boolean aEnhancement ) { - setNoEnhancement( fPointer, !aEnhancement ); - } - - public boolean isFancyUpsampling() { - return !isNoFancyUpsampling( fPointer ); - } - - public void setFancyUpsampling( boolean aFancyUpsampling ) { - setNoFancyUpsampling( fPointer, !aFancyUpsampling ); - } - - public int getScaledHeight() { - return getScaledHeight( fPointer ); - } - - public void setScaledHeight( int aScaledHeight ) { - setScaledHeight( fPointer, aScaledHeight ); + return fOptions.getCropHeight(); } public int getScaledWidth() { - return getScaledWidth( fPointer ); - } - - public void setScaledWidth( int aScaledWidth ) { - setScaledWidth( fPointer, aScaledWidth ); + return fOptions.getScaledWidth(); } public boolean isUseCropping() { - return isUseCropping( fPointer ); + return fOptions.isUseCropping(); } - public void setUseCropping( boolean aUseCropping ) { - setUseCropping( fPointer, aUseCropping ); - } - - public boolean isUseScaling() { - return isUseScaling( fPointer ); - } - - public void setUseScaling( boolean aUseScaling ) { - setUseScaling( fPointer, aUseScaling ); - } - - public boolean isUseThreads() { - return isUseThreads( fPointer ); - } - - public void setUseThreads( boolean aUseThreads ) { - setUseThreads( fPointer, aUseThreads ); + public void setCropWidth(int aCropWidth) { + fOptions.setCropWidth(aCropWidth); } public boolean isBypassFiltering() { - return isBypassFiltering( fPointer ); + return fOptions.isBypassFiltering(); } - public void setBypassFiltering( boolean aBypassFiltering ) { - setBypassFiltering( fPointer, aBypassFiltering ); + public int getCropLeft() { + return fOptions.getCropLeft(); } - private static native long createDecoderOptions(); + public int getCropWidth() { + return fOptions.getCropWidth(); + } - private static native void deleteDecoderOptions( long aPointer ); + public int getScaledHeight() { + return fOptions.getScaledHeight(); + } - private static native int getCropHeight( long aPointer ); + public void setBypassFiltering(boolean aBypassFiltering) { + fOptions.setBypassFiltering(aBypassFiltering); + } - private static native void setCropHeight( long aPointer, int aCropHeight ); + public void setUseCropping(boolean aUseCropping) { + fOptions.setUseCropping(aUseCropping); + } - private static native int getCropLeft( long aPointer ); + public void setCropHeight(int aCropHeight) { + fOptions.setCropHeight(aCropHeight); + } - private static native void setCropLeft( long aPointer, int aCropLeft ); + public void setFancyUpsampling(boolean aFancyUpsampling) { + fOptions.setFancyUpsampling(aFancyUpsampling); + } - private static native int getCropTop( long aPointer ); + public boolean isUseThreads() { + return fOptions.isUseThreads(); + } - private static native void setCropTop( long aPointer, int aCropTop ); + public boolean isFancyUpsampling() { + return fOptions.isFancyUpsampling(); + } - private static native int getCropWidth( long aPointer ); + public boolean isUseScaling() { + return fOptions.isUseScaling(); + } - private static native void setCropWidth( long aPointer, int aCropWidth ); + public void setCropLeft(int aCropLeft) { + fOptions.setCropLeft(aCropLeft); + } - private static native boolean isForceRotation( long aPointer ); + public int getCropTop() { + return fOptions.getCropTop(); + } - private static native void setForceRotation( long aPointer, boolean aForceRotation ); + public void setScaledWidth(int aScaledWidth) { + fOptions.setScaledWidth(aScaledWidth); + } - private static native boolean isNoEnhancement( long aPointer ); + public void setCropTop(int aCropTop) { + fOptions.setCropTop(aCropTop); + } - private static native void setNoEnhancement( long aPointer, boolean aNoEnhancement ); - - private static native boolean isNoFancyUpsampling( long aPointer ); - - private static native void setNoFancyUpsampling( long aPointer, boolean aFancyUpsampling ); - - private static native int getScaledHeight( long aPointer ); - - private static native void setScaledHeight( long aPointer, int aScaledHeight ); - - private static native int getScaledWidth( long aPointer ); - - private static native void setScaledWidth( long aPointer, int aScaledWidth ); - - private static native boolean isUseCropping( long aPointer ); - - private static native void setUseCropping( long aPointer, boolean aUseCropping ); - - private static native boolean isUseScaling( long aPointer ); - - private static native void setUseScaling( long aPointer, boolean aUseScaling ); - - private static native boolean isUseThreads( long aPointer ); - - private static native void setUseThreads( long aPointer, boolean aUseThreads ); - - private static native boolean isBypassFiltering( long aPointer ); - - private static native void setBypassFiltering( long aPointer, boolean aBypassFiltering ); + WebPDecoderOptions getDecoderOptions() { + return fOptions; + } } diff --git a/src/main/java/com/luciad/imageio/webp/WebPReader.java b/src/main/java/com/luciad/imageio/webp/WebPReader.java index 6a83d28..ed3809d 100644 --- a/src/main/java/com/luciad/imageio/webp/WebPReader.java +++ b/src/main/java/com/luciad/imageio/webp/WebPReader.java @@ -22,9 +22,16 @@ import javax.imageio.metadata.IIOMetadata; import javax.imageio.spi.ImageReaderSpi; import javax.imageio.stream.ImageInputStream; 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.nio.ByteOrder; import java.util.Collections; +import java.util.Hashtable; import java.util.Iterator; class WebPReader extends ImageReader { @@ -145,7 +152,26 @@ class WebPReader extends ImageReader { checkIndex( imageIndex ); readData(); readHeader(); - WebPReadParam options = param != null ? (WebPReadParam) param : new WebPReadParam(); - return WebP.decode( options, fData, 0, fData.length ); + WebPReadParam readParam = param != null ? (WebPReadParam) param : new WebPReadParam(); + + int[] outParams = new int[4]; + int[] pixels = WebP.decode(readParam.getDecoderOptions(), fData, 0, fData.length, outParams); + + int width = outParams[1]; + int height = outParams[2]; + boolean alpha = outParams[3] != 0; + + ColorModel colorModel; + if ( alpha ) { + colorModel = new DirectColorModel( 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 ); + } else { + colorModel = new DirectColorModel( 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 ); + } + + SampleModel sampleModel = colorModel.createCompatibleSampleModel( width, height ); + DataBufferInt db = new DataBufferInt( pixels, width * height ); + WritableRaster raster = WritableRaster.createWritableRaster(sampleModel, db, null); + + return new BufferedImage( colorModel, raster, false, new Hashtable() ); } } diff --git a/src/main/java/com/luciad/imageio/webp/WebPWriteParam.java b/src/main/java/com/luciad/imageio/webp/WebPWriteParam.java index 2491e95..b0ce813 100644 --- a/src/main/java/com/luciad/imageio/webp/WebPWriteParam.java +++ b/src/main/java/com/luciad/imageio/webp/WebPWriteParam.java @@ -19,45 +19,26 @@ import javax.imageio.ImageWriteParam; import java.util.Locale; public class WebPWriteParam extends ImageWriteParam { - static { - WebP.loadNativeLibrary(); - } + public static final int LOSSY_COMPRESSION = 0; + public static final int LOSSLESS_COMPRESSION = 1; - long fPointer; - private final int defaultLossless; + private final boolean fDefaultLossless; + private WebPEncoderOptions fOptions; public WebPWriteParam( Locale aLocale ) { super( aLocale ); - fPointer = createConfig(); - if ( fPointer == 0 ) { - throw new OutOfMemoryError(); - } - defaultLossless = getLossless( fPointer ); + fOptions = new WebPEncoderOptions(); + fDefaultLossless = fOptions.isLossless(); canWriteCompressed = true; compressionTypes = new String[]{ "Lossy", "Lossless" }; - compressionType = compressionTypes[defaultLossless]; - compressionQuality = getQuality( fPointer ) / 100f; + compressionType = compressionTypes[fDefaultLossless ? LOSSLESS_COMPRESSION : LOSSY_COMPRESSION]; + compressionQuality = fOptions.getCompressionQuality() / 100f; compressionMode = MODE_EXPLICIT; } - @Override - protected void finalize() throws Throwable { - super.finalize(); - deleteConfig( fPointer ); - fPointer = 0L; - } - - private static native long createConfig(); - - private static native void deleteConfig( long aPointer ); - - long getPointer() { - return fPointer; - } - @Override public float getCompressionQuality() { return super.getCompressionQuality(); @@ -66,7 +47,7 @@ public class WebPWriteParam extends ImageWriteParam { @Override public void setCompressionQuality( float quality ) { super.setCompressionQuality( quality ); - setQuality( fPointer, quality * 100f ); + fOptions.setCompressionQuality( quality * 100f ); } @Override @@ -74,7 +55,7 @@ public class WebPWriteParam extends ImageWriteParam { super.setCompressionType( compressionType ); for ( int i = 0; i < compressionTypes.length; i++ ) { if ( compressionTypes[i].equals( compressionType ) ) { - setLossless( fPointer, i ); + fOptions.setLossless( i == LOSSLESS_COMPRESSION ); break; } } @@ -84,254 +65,170 @@ public class WebPWriteParam extends ImageWriteParam { @Override public void unsetCompression() { super.unsetCompression(); - setLossless( fPointer, defaultLossless ); + fOptions.setLossless( fDefaultLossless ); } - public int getTargetSize() { - return getTargetSize( fPointer ); + public void setSnsStrength(int aSnsStrength) { + fOptions.setSnsStrength(aSnsStrength); } - public void setTargetSize( int aTargetSize ) { - setTargetSize( fPointer, aTargetSize ); - } - - public float getTargetPSNR() { - return getTargetPSNR( fPointer ); - } - - public void setTargetPSNR( float aTargetPSNR ) { - setTargetPSNR( fPointer, aTargetPSNR ); - } - - public int getMethod() { - return getMethod( fPointer ); - } - - public void setMethod( int aMethod ) { - setMethod( fPointer, aMethod ); + public void setAlphaQuality(int aAlphaQuality) { + fOptions.setAlphaQuality(aAlphaQuality); } public int getSegments() { - return getSegments( fPointer ); - } - - public void setSegments( int aSegments ) { - setSegments( fPointer, aSegments ); - } - - public int getSnsStrength() { - return getSnsStrength( fPointer ); - } - - public void setSnsStrength( int aSnsStrength ) { - setSnsStrength( fPointer, aSnsStrength ); - } - - public int getFilterStrength() { - return getFilterStrength( fPointer ); - } - - public void setFilterStrength( int aFilterStrength ) { - setFilterStrength( fPointer, aFilterStrength ); - } - - public int getFilterSharpness() { - return getFilterSharpness( fPointer ); - } - - public void setFilterSharpness( int aFilterSharpness ) { - setFilterSharpness( fPointer, aFilterSharpness ); - } - - public int getFilterType() { - return getFilterType( fPointer ); - } - - public void setFilterType( int aFilterType ) { - setFilterType( fPointer, aFilterType ); - } - - public boolean isAutoAdjustFilterStrength() { - return getAutofilter( fPointer ) != 0; - } - - public void setAutoAdjustFilterStrength( boolean aAutofilter ) { - setAutofilter( fPointer, aAutofilter ? 1 : 0 ); - } - - public int getEntropyAnalysisPassCount() { - return getPass( fPointer ); - } - - public void setEntropyAnalysisPassCount( int aPass ) { - setPass( fPointer, aPass ); - } - - public boolean isShowCompressed() { - return getShowCompressed( fPointer ) != 0; - } - - public void setShowCompressed( boolean aShowCompressed ) { - setShowCompressed( fPointer, aShowCompressed ? 1 : 0 ); + return fOptions.getSegments(); } public int getPreprocessing() { - return getPreprocessing( fPointer ); + return fOptions.getPreprocessing(); } - public void setPreprocessing( int aPreprocessing ) { - setPreprocessing( fPointer, aPreprocessing ); + public int getFilterStrength() { + return fOptions.getFilterStrength(); + } + + public void setEmulateJpegSize(boolean aEmulateJpegSize) { + fOptions.setEmulateJpegSize(aEmulateJpegSize); } public int getPartitions() { - return getPartitions( fPointer ); + return fOptions.getPartitions(); } - public void setPartitions( int aPartitions ) { - setPartitions( fPointer, aPartitions ); + public void setTargetPSNR(float aTargetPSNR) { + fOptions.setTargetPSNR(aTargetPSNR); + } + + public int getEntropyAnalysisPassCount() { + return fOptions.getEntropyAnalysisPassCount(); } public int getPartitionLimit() { - return getPartitionLimit( fPointer ); + return fOptions.getPartitionLimit(); } - public void setPartitionLimit( int aPartitionLimit ) { - setPartitionLimit( fPointer, aPartitionLimit ); + public int getFilterType() { + return fOptions.getFilterType(); } - public int getAlphaCompression() { - return getAlphaCompression( fPointer ); - } - - public void setAlphaCompression( int aAlphaCompression ) { - setAlphaCompression( fPointer, aAlphaCompression ); - } - - public int getAlphaFiltering() { - return getAlphaFiltering( fPointer ); - } - - public void setAlphaFiltering( int aAlphaFiltering ) { - setAlphaFiltering( fPointer, aAlphaFiltering ); + public int getFilterSharpness() { + return fOptions.getFilterSharpness(); } public int getAlphaQuality() { - return getAlphaQuality( fPointer ); + return fOptions.getAlphaQuality(); } - public void setAlphaQuality( int aAlphaQuality ) { - setAlphaQuality( fPointer, aAlphaQuality ); - } - - public boolean isEmulateJpegSize() { - return getEmulateJpegSize( fPointer ) != 0; - } - - public void setEmulateJpegSize( boolean aEmulateJpegSize ) { - setEmulateJpegSize( fPointer, aEmulateJpegSize ? 1 : 0 ); - } - - public int getThreadLevel() { - return getThreadLevel( fPointer ); - } - - public void setThreadLevel( int aThreadLevel ) { - setThreadLevel( fPointer, aThreadLevel ); + public boolean isShowCompressed() { + return fOptions.isShowCompressed(); } public boolean isReduceMemoryUsage() { - return getLowMemory( fPointer ) != 0; + return fOptions.isReduceMemoryUsage(); } - public void setReduceMemoryUsage( boolean aLowMemory ) { - setLowMemory( fPointer, aLowMemory ? 1 : 0 ); + public void setThreadLevel(int aThreadLevel) { + fOptions.setThreadLevel(aThreadLevel); } - private static native float getQuality( long aPointer ); + public boolean isAutoAdjustFilterStrength() { + return fOptions.isAutoAdjustFilterStrength(); + } - private static native void setQuality( long aPointer, float aQuality ); + public void setReduceMemoryUsage(boolean aLowMemory) { + fOptions.setReduceMemoryUsage(aLowMemory); + } - private static native int getTargetSize( long aPointer ); + public void setFilterStrength(int aFilterStrength) { + fOptions.setFilterStrength(aFilterStrength); + } - private static native void setTargetSize( long aPointer, int aTargetSize ); + public int getTargetSize() { + return fOptions.getTargetSize(); + } - private static native float getTargetPSNR( long aPointer ); + public void setEntropyAnalysisPassCount(int aPass) { + fOptions.setEntropyAnalysisPassCount(aPass); + } - private static native void setTargetPSNR( long aPointer, float aTargetPSNR ); + public void setFilterSharpness(int aFilterSharpness) { + fOptions.setFilterSharpness(aFilterSharpness); + } - private static native int getMethod( long aPointer ); + public int getAlphaFiltering() { + return fOptions.getAlphaFiltering(); + } - private static native void setMethod( long aPointer, int aMethod ); + public int getSnsStrength() { + return fOptions.getSnsStrength(); + } - private static native int getSegments( long aPointer ); + public void setPartitionLimit(int aPartitionLimit) { + fOptions.setPartitionLimit(aPartitionLimit); + } - private static native void setSegments( long aPointer, int aSegments ); + public void setMethod(int aMethod) { + fOptions.setMethod(aMethod); + } - private static native int getSnsStrength( long aPointer ); + public void setAlphaFiltering(int aAlphaFiltering) { + fOptions.setAlphaFiltering(aAlphaFiltering); + } - private static native void setSnsStrength( long aPointer, int aSnsStrength ); + public int getMethod() { + return fOptions.getMethod(); + } - private static native int getFilterStrength( long aPointer ); + public void setFilterType(int aFilterType) { + fOptions.setFilterType(aFilterType); + } - private static native void setFilterStrength( long aPointer, int aFilterStrength ); + public void setPartitions(int aPartitions) { + fOptions.setPartitions(aPartitions); + } - private static native int getFilterSharpness( long aPointer ); + public void setAutoAdjustFilterStrength(boolean aAutofilter) { + fOptions.setAutoAdjustFilterStrength(aAutofilter); + } - private static native void setFilterSharpness( long aPointer, int aFilterSharpness ); + public boolean isEmulateJpegSize() { + return fOptions.isEmulateJpegSize(); + } - private static native int getFilterType( long aPointer ); + public int getAlphaCompression() { + return fOptions.getAlphaCompression(); + } - private static native void setFilterType( long aPointer, int aFilterType ); + public void setShowCompressed(boolean aShowCompressed) { + fOptions.setShowCompressed(aShowCompressed); + } - private static native int getAutofilter( long aPointer ); + public void setSegments(int aSegments) { + fOptions.setSegments(aSegments); + } - private static native void setAutofilter( long aPointer, int aAutofilter ); + public float getTargetPSNR() { + return fOptions.getTargetPSNR(); + } - private static native int getPass( long aPointer ); + public int getThreadLevel() { + return fOptions.getThreadLevel(); + } - private static native void setPass( long aPointer, int aPass ); + public void setTargetSize(int aTargetSize) { + fOptions.setTargetSize(aTargetSize); + } - private static native int getShowCompressed( long aPointer ); + public void setAlphaCompression(int aAlphaCompression) { + fOptions.setAlphaCompression(aAlphaCompression); + } - private static native void setShowCompressed( long aPointer, int aShowCompressed ); + public void setPreprocessing(int aPreprocessing) { + fOptions.setPreprocessing(aPreprocessing); + } - private static native int getPreprocessing( long aPointer ); - - private static native void setPreprocessing( long aPointer, int aPreprocessing ); - - private static native int getPartitions( long aPointer ); - - private static native void setPartitions( long aPointer, int aPartitions ); - - private static native int getPartitionLimit( long aPointer ); - - private static native void setPartitionLimit( long aPointer, int aPartitionLimit ); - - private static native int getAlphaCompression( long aPointer ); - - private static native void setAlphaCompression( long aPointer, int aAlphaCompression ); - - private static native int getAlphaFiltering( long aPointer ); - - private static native void setAlphaFiltering( long aPointer, int aAlphaFiltering ); - - private static native int getAlphaQuality( long aPointer ); - - private static native void setAlphaQuality( long aPointer, int aAlphaQuality ); - - private static native int getLossless( long aPointer ); - - private static native void setLossless( long aPointer, int aLossless ); - - private static native int getEmulateJpegSize( long aPointer ); - - private static native void setEmulateJpegSize( long aPointer, int aEmulateJpegSize ); - - private static native int getThreadLevel( long aPointer ); - - private static native void setThreadLevel( long aPointer, int aThreadLevel ); - - private static native int getLowMemory( long aPointer ); - - private static native void setLowMemory( long aPointer, int aLowMemory ); + WebPEncoderOptions getEncoderOptions() { + return fOptions; + } } diff --git a/src/main/java/com/luciad/imageio/webp/WebPWriter.java b/src/main/java/com/luciad/imageio/webp/WebPWriter.java index 795fb4d..9e7e837 100644 --- a/src/main/java/com/luciad/imageio/webp/WebPWriter.java +++ b/src/main/java/com/luciad/imageio/webp/WebPWriter.java @@ -22,51 +22,351 @@ import javax.imageio.ImageWriter; import javax.imageio.metadata.IIOMetadata; import javax.imageio.spi.ImageWriterSpi; import javax.imageio.stream.ImageOutputStream; -import java.awt.image.RenderedImage; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.image.*; import java.io.IOException; class WebPWriter extends ImageWriter { - WebPWriter( ImageWriterSpi originatingProvider ) { - super( originatingProvider ); + WebPWriter(ImageWriterSpi originatingProvider) { + super(originatingProvider); } @Override public ImageWriteParam getDefaultWriteParam() { - return new WebPWriteParam( getLocale() ); + return new WebPWriteParam(getLocale()); } @Override - public IIOMetadata convertImageMetadata( IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param ) { + public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) { return null; } @Override - public IIOMetadata convertStreamMetadata( IIOMetadata inData, ImageWriteParam param ) { + public IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param) { return null; } @Override - public IIOMetadata getDefaultImageMetadata( ImageTypeSpecifier imageType, ImageWriteParam param ) { + public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) { return null; } @Override - public IIOMetadata getDefaultStreamMetadata( ImageWriteParam param ) { + public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { return null; } @Override - public void write( IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param ) throws IOException { - if ( param == null ) { + public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException { + if (param == null) { param = getDefaultWriteParam(); } WebPWriteParam writeParam = (WebPWriteParam) param; - ImageOutputStream output = ( ImageOutputStream ) getOutput(); + ImageOutputStream output = (ImageOutputStream) getOutput(); RenderedImage ri = image.getRenderedImage(); - byte[] encodedData = WebP.encode(writeParam, ri); - output.write( encodedData ); + byte[] encodedData = encode(writeParam.getEncoderOptions(), ri); + output.write(encodedData); + } + + private static byte[] encode(WebPEncoderOptions aOptions, RenderedImage aImage) throws IOException + { + if (aOptions == null) { + throw new NullPointerException("Encoder options may not be null"); + } + + if (aImage == null) { + throw new NullPointerException("Image may not be null"); + } + + boolean encodeAlpha = hasTranslucency(aImage); + if (encodeAlpha) { + byte[] rgbaData = getRGBA(aImage); + return WebP.encodeRGBA(aOptions, rgbaData, aImage.getWidth(), aImage.getHeight(), aImage.getWidth() * 4); + } else { + byte[] rgbData = getRGB(aImage); + return WebP.encodeRGB(aOptions, rgbData, aImage.getWidth(), aImage.getHeight(), aImage.getWidth() * 3); + } + } + + private static boolean hasTranslucency(RenderedImage aRi) { + return aRi.getColorModel().hasAlpha(); + } + + private static int getShift(int aMask) { + int shift = 0; + while (((aMask >> shift) & 0x1) == 0) { + shift++; + } + return shift; + } + + private static byte[] getRGB(RenderedImage aRi) throws IOException { + int width = aRi.getWidth(); + int height = aRi.getHeight(); + + ColorModel colorModel = aRi.getColorModel(); + if (colorModel instanceof ComponentColorModel) { + ComponentSampleModel sampleModel = (ComponentSampleModel) aRi.getSampleModel(); + int type = sampleModel.getTransferType(); + if (type == DataBuffer.TYPE_BYTE) { + return extractComponentRGBByte(width, height, sampleModel, ((DataBufferByte) aRi.getData().getDataBuffer())); + } else if (type == DataBuffer.TYPE_INT) { + return extractComponentRGBInt(width, height, sampleModel, ((DataBufferInt) aRi.getData().getDataBuffer())); + } else { + throw new IOException("Incompatible image: " + aRi); + } + } else if (colorModel instanceof DirectColorModel) { + SinglePixelPackedSampleModel sampleModel = (SinglePixelPackedSampleModel) aRi.getSampleModel(); + int type = sampleModel.getTransferType(); + if (type == DataBuffer.TYPE_INT) { + return extractDirectRGBInt(width, height, (DirectColorModel) colorModel, sampleModel, ((DataBufferInt) aRi.getData().getDataBuffer())); + } else { + throw new IOException("Incompatible image: " + aRi); + } + } else { + BufferedImage i = new BufferedImage(aRi.getWidth(), aRi.getHeight(), BufferedImage.TYPE_INT_RGB); + Graphics2D g = i.createGraphics(); + g.drawRenderedImage(aRi, new AffineTransform()); + g.dispose(); + return getRGB(i); + } + } + + private static byte[] extractDirectRGBInt(int aWidth, int aHeight, DirectColorModel aColorModel, SinglePixelPackedSampleModel aSampleModel, DataBufferInt aDataBuffer) { + byte[] out = new byte[aWidth * aHeight * 3]; + + int rMask = aColorModel.getRedMask(); + int gMask = aColorModel.getGreenMask(); + int bMask = aColorModel.getBlueMask(); + int rShift = getShift(rMask); + int gShift = getShift(gMask); + int bShift = getShift(bMask); + int[] bank = aDataBuffer.getBankData()[0]; + int scanlineStride = aSampleModel.getScanlineStride(); + int scanIx = 0; + for (int b = 0, y = 0; y < aHeight; y++) { + int pixIx = scanIx; + for (int x = 0; x < aWidth; x++, b += 3) { + int pixel = bank[pixIx++]; + out[b] = (byte) ((pixel & rMask) >>> rShift); + out[b + 1] = (byte) ((pixel & gMask) >>> gShift); + out[b + 2] = (byte) ((pixel & bMask) >>> bShift); + } + scanIx += scanlineStride; + } + return out; + } + + private static byte[] extractComponentRGBInt(int aWidth, int aHeight, ComponentSampleModel aSampleModel, DataBufferInt aDataBuffer) { + byte[] out = new byte[aWidth * aHeight * 3]; + + int[] bankIndices = aSampleModel.getBankIndices(); + int[] rBank = aDataBuffer.getBankData()[bankIndices[0]]; + int[] gBank = aDataBuffer.getBankData()[bankIndices[1]]; + int[] bBank = aDataBuffer.getBankData()[bankIndices[2]]; + + int[] bankOffsets = aSampleModel.getBandOffsets(); + int rScanIx = bankOffsets[0]; + int gScanIx = bankOffsets[1]; + int bScanIx = bankOffsets[2]; + + int pixelStride = aSampleModel.getPixelStride(); + int scanlineStride = aSampleModel.getScanlineStride(); + for (int b = 0, y = 0; y < aHeight; y++) { + int rPixIx = rScanIx; + int gPixIx = gScanIx; + int bPixIx = bScanIx; + for (int x = 0; x < aWidth; x++, b += 3) { + out[b] = (byte) rBank[rPixIx]; + rPixIx += pixelStride; + out[b + 1] = (byte) gBank[gPixIx]; + gPixIx += pixelStride; + out[b + 2] = (byte) bBank[bPixIx]; + bPixIx += pixelStride; + } + rScanIx += scanlineStride; + gScanIx += scanlineStride; + bScanIx += scanlineStride; + } + return out; + } + + private static byte[] extractComponentRGBByte(int aWidth, int aHeight, ComponentSampleModel aSampleModel, DataBufferByte aDataBuffer) { + byte[] out = new byte[aWidth * aHeight * 3]; + + int[] bankIndices = aSampleModel.getBankIndices(); + byte[] rBank = aDataBuffer.getBankData()[bankIndices[0]]; + byte[] gBank = aDataBuffer.getBankData()[bankIndices[1]]; + byte[] bBank = aDataBuffer.getBankData()[bankIndices[2]]; + + int[] bankOffsets = aSampleModel.getBandOffsets(); + int rScanIx = bankOffsets[0]; + int gScanIx = bankOffsets[1]; + int bScanIx = bankOffsets[2]; + + int pixelStride = aSampleModel.getPixelStride(); + int scanlineStride = aSampleModel.getScanlineStride(); + for (int b = 0, y = 0; y < aHeight; y++) { + int rPixIx = rScanIx; + int gPixIx = gScanIx; + int bPixIx = bScanIx; + for (int x = 0; x < aWidth; x++, b += 3) { + out[b] = rBank[rPixIx]; + rPixIx += pixelStride; + out[b + 1] = gBank[gPixIx]; + gPixIx += pixelStride; + out[b + 2] = bBank[bPixIx]; + bPixIx += pixelStride; + } + rScanIx += scanlineStride; + gScanIx += scanlineStride; + bScanIx += scanlineStride; + } + return out; + } + + private static byte[] getRGBA(RenderedImage aRi) throws IOException { + int width = aRi.getWidth(); + int height = aRi.getHeight(); + + ColorModel colorModel = aRi.getColorModel(); + if (colorModel instanceof ComponentColorModel) { + ComponentSampleModel sampleModel = (ComponentSampleModel) aRi.getSampleModel(); + int type = sampleModel.getTransferType(); + if (type == DataBuffer.TYPE_BYTE) { + return extractComponentRGBAByte(width, height, sampleModel, ((DataBufferByte) aRi.getData().getDataBuffer())); + } else if (type == DataBuffer.TYPE_INT) { + return extractComponentRGBAInt(width, height, sampleModel, ((DataBufferInt) aRi.getData().getDataBuffer())); + } else { + throw new IOException("Incompatible image: " + aRi); + } + } else if (colorModel instanceof DirectColorModel) { + SinglePixelPackedSampleModel sampleModel = (SinglePixelPackedSampleModel) aRi.getSampleModel(); + int type = sampleModel.getTransferType(); + if (type == DataBuffer.TYPE_INT) { + return extractDirectRGBAInt(width, height, (DirectColorModel) colorModel, sampleModel, ((DataBufferInt) aRi.getData().getDataBuffer())); + } else { + throw new IOException("Incompatible image: " + aRi); + } + } else { + BufferedImage i = new BufferedImage(aRi.getWidth(), aRi.getHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics2D g = i.createGraphics(); + g.drawRenderedImage(aRi, new AffineTransform()); + g.dispose(); + return getRGBA(i); + } + } + + private static byte[] extractDirectRGBAInt(int aWidth, int aHeight, DirectColorModel aColorModel, SinglePixelPackedSampleModel aSampleModel, DataBufferInt aDataBuffer) { + byte[] out = new byte[aWidth * aHeight * 4]; + + int rMask = aColorModel.getRedMask(); + int gMask = aColorModel.getGreenMask(); + int bMask = aColorModel.getBlueMask(); + int aMask = aColorModel.getAlphaMask(); + int rShift = getShift(rMask); + int gShift = getShift(gMask); + int bShift = getShift(bMask); + int aShift = getShift(aMask); + int[] bank = aDataBuffer.getBankData()[0]; + int scanlineStride = aSampleModel.getScanlineStride(); + int scanIx = 0; + for (int b = 0, y = 0; y < aHeight; y++) { + int pixIx = scanIx; + for (int x = 0; x < aWidth; x++, b += 4) { + int pixel = bank[pixIx++]; + out[b] = (byte) ((pixel & rMask) >>> rShift); + out[b + 1] = (byte) ((pixel & gMask) >>> gShift); + out[b + 2] = (byte) ((pixel & bMask) >>> bShift); + out[b + 3] = (byte) ((pixel & aMask) >>> aShift); + } + scanIx += scanlineStride; + } + return out; + } + + private static byte[] extractComponentRGBAInt(int aWidth, int aHeight, ComponentSampleModel aSampleModel, DataBufferInt aDataBuffer) { + byte[] out = new byte[aWidth * aHeight * 4]; + + int[] bankIndices = aSampleModel.getBankIndices(); + int[] rBank = aDataBuffer.getBankData()[bankIndices[0]]; + int[] gBank = aDataBuffer.getBankData()[bankIndices[1]]; + int[] bBank = aDataBuffer.getBankData()[bankIndices[2]]; + int[] aBank = aDataBuffer.getBankData()[bankIndices[3]]; + + int[] bankOffsets = aSampleModel.getBandOffsets(); + int rScanIx = bankOffsets[0]; + int gScanIx = bankOffsets[1]; + int bScanIx = bankOffsets[2]; + int aScanIx = bankOffsets[3]; + + int pixelStride = aSampleModel.getPixelStride(); + int scanlineStride = aSampleModel.getScanlineStride(); + for (int b = 0, y = 0; y < aHeight; y++) { + int rPixIx = rScanIx; + int gPixIx = gScanIx; + int bPixIx = bScanIx; + int aPixIx = aScanIx; + for (int x = 0; x < aWidth; x++, b += 4) { + out[b] = (byte) rBank[rPixIx]; + rPixIx += pixelStride; + out[b + 1] = (byte) gBank[gPixIx]; + gPixIx += pixelStride; + out[b + 2] = (byte) bBank[bPixIx]; + bPixIx += pixelStride; + out[b + 3] = (byte) aBank[aPixIx]; + aPixIx += pixelStride; + } + rScanIx += scanlineStride; + gScanIx += scanlineStride; + bScanIx += scanlineStride; + aScanIx += scanlineStride; + } + return out; + } + + private static byte[] extractComponentRGBAByte(int aWidth, int aHeight, ComponentSampleModel aSampleModel, DataBufferByte aDataBuffer) { + byte[] out = new byte[aWidth * aHeight * 4]; + + int[] bankIndices = aSampleModel.getBankIndices(); + byte[] rBank = aDataBuffer.getBankData()[bankIndices[0]]; + byte[] gBank = aDataBuffer.getBankData()[bankIndices[1]]; + byte[] bBank = aDataBuffer.getBankData()[bankIndices[2]]; + byte[] aBank = aDataBuffer.getBankData()[bankIndices[3]]; + + int[] bankOffsets = aSampleModel.getBandOffsets(); + int rScanIx = bankOffsets[0]; + int gScanIx = bankOffsets[1]; + int bScanIx = bankOffsets[2]; + int aScanIx = bankOffsets[3]; + + int pixelStride = aSampleModel.getPixelStride(); + int scanlineStride = aSampleModel.getScanlineStride(); + for (int b = 0, y = 0; y < aHeight; y++) { + int rPixIx = rScanIx; + int gPixIx = gScanIx; + int bPixIx = bScanIx; + int aPixIx = aScanIx; + for (int x = 0; x < aWidth; x++, b += 4) { + out[b] = rBank[rPixIx]; + rPixIx += pixelStride; + out[b + 1] = gBank[gPixIx]; + gPixIx += pixelStride; + out[b + 2] = bBank[bPixIx]; + bPixIx += pixelStride; + out[b + 3] = aBank[aPixIx]; + aPixIx += pixelStride; + } + rScanIx += scanlineStride; + gScanIx += scanlineStride; + bScanIx += scanlineStride; + aScanIx += scanlineStride; + } + return out; } } diff --git a/src/main/java/example/EncodeTest.java b/src/main/java/example/EncodeTest.java index 699bf19..93134dd 100644 --- a/src/main/java/example/EncodeTest.java +++ b/src/main/java/example/EncodeTest.java @@ -30,6 +30,8 @@ public class EncodeTest { writer.setOutput(new FileImageOutputStream(new File(outputWebpPath))); // Encode + long st = System.currentTimeMillis(); writer.write(null, new IIOImage(image, null, null), writeParam); + System.out.println("cost: " + (System.currentTimeMillis() - st)); } } diff --git a/src/main/resources/META-INF/lib/linux_64/libwebp-imageio.so b/src/main/resources/META-INF/lib/linux_64/libwebp-imageio.so index cc668d1..93f7e8d 100755 Binary files a/src/main/resources/META-INF/lib/linux_64/libwebp-imageio.so and b/src/main/resources/META-INF/lib/linux_64/libwebp-imageio.so differ diff --git a/src/main/resources/META-INF/lib/windows_32/webp-imageio.dll b/src/main/resources/META-INF/lib/windows_32/webp-imageio.dll index 636b6a8..3811223 100755 Binary files a/src/main/resources/META-INF/lib/windows_32/webp-imageio.dll and b/src/main/resources/META-INF/lib/windows_32/webp-imageio.dll differ diff --git a/src/main/resources/META-INF/lib/windows_64/webp-imageio.dll b/src/main/resources/META-INF/lib/windows_64/webp-imageio.dll index 1944b81..61bab7a 100755 Binary files a/src/main/resources/META-INF/lib/windows_64/webp-imageio.dll and b/src/main/resources/META-INF/lib/windows_64/webp-imageio.dll differ