summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coders/webp.c385
-rwxr-xr-xconfigure6
-rwxr-xr-xconfigure.ac1
-rwxr-xr-xpackaging/GraphicsMagick.spec1
4 files changed, 333 insertions, 60 deletions
diff --git a/coders/webp.c b/coders/webp.c
index 04016e7..711fcf5 100644
--- a/coders/webp.c
+++ b/coders/webp.c
@@ -61,6 +61,7 @@ static unsigned int WriteWEBPImage(const ImageInfo *,Image *);
#if defined(HasWEBP)
#include <webp/decode.h>
#include <webp/encode.h>
+#include <webp/demux.h>
/*
Release versions vs ABI versions (found in src/webp/encode.h)
@@ -138,6 +139,252 @@ static MagickTsdKey_t tsd_key = (MagickTsdKey_t) 0;
% o exception: return any errors or warnings in this structure.
%
*/
+
+/*
+ Apply ImageMagick patch for animated WebP.
+ Decoding only the first frame of the animation image is supported.
+*/
+#ifndef __TIZEN__
+#define __TIZEN__
+#endif
+
+#if defined(__TIZEN__)
+static inline uint32_t ReadWebPLSBWord(
+ const unsigned char *restrict data)
+{
+ register const unsigned char
+ *p;
+
+ register uint32_t
+ value;
+
+ p=data;
+ value=(uint32_t) (*p++);
+ value|=((uint32_t) (*p++)) << 8;
+ value|=((uint32_t) (*p++)) << 16;
+ value|=((uint32_t) (*p++)) << 24;
+ return(value);
+}
+
+static int FillBasicWEBPInfo(Image *image,const uint8_t *stream,size_t length,
+ WebPDecoderConfig *configure)
+{
+ WebPBitstreamFeatures
+ *restrict features = &configure->input;
+
+ int
+ webp_status;
+
+ webp_status=WebPGetFeatures(stream,length,features);
+
+ if (webp_status != VP8_STATUS_OK)
+ return(webp_status);
+
+ image->columns=(size_t) features->width;
+ image->rows=(size_t) features->height;
+ image->depth=8;
+ image->matte=(features->has_alpha ? MagickTrue : MagickFalse);
+
+ return(webp_status);
+}
+
+static int ReadSingleWEBPImage(Image *image,const uint8_t *stream,
+ size_t length,WebPDecoderConfig *configure,ExceptionInfo *exception,
+ MagickBool is_first)
+{
+ int
+ webp_status;
+
+ register unsigned char
+ *p;
+
+ register PixelPacket
+ *q;
+
+ register size_t
+ x;
+
+ size_t
+ canvas_width,
+ canvas_height,
+ image_width,
+ image_height;
+
+ ssize_t
+ x_offset,
+ y_offset,
+ y;
+
+ WebPDecBuffer
+ *restrict webp_image = &configure->output;
+
+ if (is_first)
+ {
+ canvas_width=image->columns;
+ canvas_height=image->rows;
+ x_offset=image->page.x;
+ y_offset=image->page.y;
+ image->page.x=0;
+ image->page.y=0;
+ }
+ else
+ {
+ x_offset=0;
+ y_offset=0;
+ }
+ webp_status=FillBasicWEBPInfo(image,stream,length,configure);
+ image_width=image->columns;
+ image_height=image->rows;
+ if (is_first)
+ {
+ image->columns=canvas_width;
+ image->rows=canvas_height;
+ }
+
+ if (webp_status != VP8_STATUS_OK)
+ return(webp_status);
+
+ webp_status=WebPDecode(stream,length,configure);
+ (void) LogMagickEvent(CoderEvent,GetMagickModule(),"WebPDecode webp_status=%d",webp_status);
+ if (webp_status != VP8_STATUS_OK)
+ return(webp_status);
+
+ p=(unsigned char *) webp_image->u.RGBA.rgba;
+
+ for (y=0; y < (size_t) image->rows; y++)
+ {
+ q=GetImagePixelsEx(image,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+
+ for (x=0; x < (size_t) image->columns; x++)
+ {
+ SetRedSample(q,ScaleCharToQuantum(*p++));
+ SetGreenSample(q,ScaleCharToQuantum(*p++));
+ SetBlueSample(q,ScaleCharToQuantum(*p++));
+ if (image->matte)
+ SetOpacitySample(q,MaxRGB-ScaleCharToQuantum(*p++));
+ else
+ SetOpacitySample(q,OpaqueOpacity);
+ q++;
+ }
+
+ if (!SyncImagePixels(image))
+ break;
+ }
+
+ WebPFreeDecBuffer(webp_image);
+#if defined(MAGICKCORE_WEBPMUX_DELEGATE)
+ {
+ uint32_t
+ webp_flags = 0;
+
+ WebPData
+ chunk,
+ content;
+
+ WebPMux
+ *mux;
+
+ /*
+ Extract any profiles.
+ */
+ content.bytes=stream;
+ content.size=length;
+ mux=WebPMuxCreate(&content,0);
+ (void) memset(&chunk,0,sizeof(chunk));
+ WebPMuxGetFeatures(mux,&webp_flags);
+ if (webp_flags & ICCP_FLAG)
+ {
+ WebPMuxGetChunk(mux,"ICCP",&chunk);
+ AppendImageProfile(image,"ICC",chunk.bytes,chunk.size);
+ }
+ if (webp_flags & EXIF_FLAG)
+ {
+ WebPMuxGetChunk(mux,"EXIF",&chunk);
+ AppendImageProfile(image,"EXIF",chunk.bytes,chunk.size);
+ }
+ if (webp_flags & XMP_FLAG)
+ {
+ WebPMuxGetChunk(mux,"XMP",&chunk);
+ AppendImageProfile(image,"XMP",chunk.bytes,chunk.size);
+ }
+ WebPMuxDelete(mux);
+ }
+#endif
+ return(webp_status);
+}
+
+static int ReadAnimatedWEBPImage(const ImageInfo *image_info,Image *image,
+ uint8_t *stream,size_t length,WebPDecoderConfig *configure,
+ ExceptionInfo *exception)
+{
+ Image
+ *original_image;
+
+ int
+ image_count,
+ webp_status;
+
+ size_t
+ canvas_width,
+ canvas_height;
+
+ WebPData
+ data;
+
+ WebPDemuxer
+ *demux;
+
+ WebPIterator
+ iter;
+
+ image_count=0;
+ webp_status=0;
+ original_image=image;
+ webp_status=FillBasicWEBPInfo(image,stream,length,configure);
+ canvas_width=image->columns;
+ canvas_height=image->rows;
+ data.bytes=stream;
+ data.size=length;
+ demux=WebPDemux(&data);
+ if (WebPDemuxGetFrame(demux,1,&iter)) {
+ do {
+ if (image_count != 0)
+ {
+ if (GetNextImageInList(image) == (Image *) NULL)
+ break;
+ image=SyncNextImageInList(image);
+ CloneImageAttributes(image,original_image);
+ image->page.x=iter.x_offset;
+ image->page.y=iter.y_offset;
+ webp_status=ReadSingleWEBPImage(image,iter.fragment.bytes,
+ iter.fragment.size,configure,exception,MagickFalse);
+ }
+ else
+ {
+ image->page.x=iter.x_offset;
+ image->page.y=iter.y_offset;
+ webp_status=ReadSingleWEBPImage(image,iter.fragment.bytes,
+ iter.fragment.size,configure,exception,MagickTrue);
+ }
+ if (webp_status != VP8_STATUS_OK)
+ break;
+
+ image->page.width=canvas_width;
+ image->page.height=canvas_height;
+ image->delay=iter.duration/10;
+ if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND)
+ image->dispose=BackgroundDispose;
+ image_count++;
+ } while (WebPDemuxNextFrame(&iter));
+ WebPDemuxReleaseIterator(&iter);
+ }
+ WebPDemuxDelete(demux);
+ return(webp_status);
+}
+#endif /* defined(__TIZEN__) */
+
static Image *ReadWEBPImage(const ImageInfo *image_info,
ExceptionInfo *exception)
{
@@ -164,6 +411,11 @@ static Image *ReadWEBPImage(const ImageInfo *image_info,
*stream,
*pixels;
+#if defined(__TIZEN__)
+ WebPDecoderConfig
+ configure;
+#endif /* defined(__TIZEN__) */
+
WebPBitstreamFeatures
stream_features;
@@ -195,7 +447,15 @@ static Image *ReadWEBPImage(const ImageInfo *image_info,
MagickFreeMemory(stream);
ThrowReaderException(CorruptImageError,InsufficientImageDataInFile,image);
}
- if ((webp_status=WebPGetFeatures(stream,length,&stream_features)) != VP8_STATUS_OK)
+
+#if defined(__TIZEN__)
+ memset(&configure, 0x00, sizeof(WebPDecoderConfig));
+ webp_status=FillBasicWEBPInfo(image,stream,length,&configure);
+ memcpy(&stream_features,&configure.input,sizeof(WebPBitstreamFeatures));
+#else
+ webp_status=WebPGetFeatures(stream,length,&stream_features);
+#endif /* defined(__TIZEN__) */
+ if (webp_status != VP8_STATUS_OK)
{
MagickFreeMemory(stream);
@@ -244,6 +504,7 @@ static Image *ReadWEBPImage(const ImageInfo *image_info,
*/
ThrowReaderException(CorruptImageError,CorruptImage,image);
}
+
image->depth=8;
image->columns=(size_t) stream_features.width;
image->rows=(size_t) stream_features.height;
@@ -259,79 +520,91 @@ static Image *ReadWEBPImage(const ImageInfo *image_info,
MagickFreeMemory(stream);
ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
}
- if (image->matte)
- pixels=(unsigned char *) WebPDecodeRGBA(stream,length,
+
+#if defined(__TIZEN__)
+ if (configure.input.has_animation) {
+ webp_status=ReadAnimatedWEBPImage(image_info,image,stream,length,
+ &configure,exception);
+ (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+ "ReadAnimatedWEBPImage return %d",webp_status);
+ } else
+#endif /* defined(__TIZEN__) */
+ {
+ if (image->matte)
+ pixels=(unsigned char *) WebPDecodeRGBA(stream,length,
&stream_features.width,
&stream_features.height);
- else
- pixels=(unsigned char *) WebPDecodeRGB(stream,length,
+ else
+ pixels=(unsigned char *) WebPDecodeRGB(stream,length,
&stream_features.width,
&stream_features.height);
- if (pixels == (unsigned char *) NULL)
- {
- MagickFreeMemory(stream);
- ThrowReaderException(CoderError,NoDataReturned,image);
- }
+ if (pixels == (unsigned char *) NULL)
+ {
+ MagickFreeMemory(stream);
+ ThrowReaderException(CoderError,NoDataReturned,image);
+ }
- p=pixels;
+ p=pixels;
- for (y=0; y < (size_t) image->rows; y++)
- {
- q=GetImagePixelsEx(image,0,y,image->columns,1,exception);
- if (q == (PixelPacket *) NULL)
- break;
+ for (y=0; y < (size_t) image->rows; y++)
+ {
+ q=GetImagePixelsEx(image,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
- for (x=0; x < (size_t) image->columns; x++)
- {
- SetRedSample(q,ScaleCharToQuantum(*p++));
- SetGreenSample(q,ScaleCharToQuantum(*p++));
- SetBlueSample(q,ScaleCharToQuantum(*p++));
- if (image->matte)
- SetOpacitySample(q,MaxRGB-ScaleCharToQuantum(*p++));
- else
- SetOpacitySample(q,OpaqueOpacity);
- q++;
- }
+ for (x=0; x < (size_t) image->columns; x++)
+ {
+ SetRedSample(q,ScaleCharToQuantum(*p++));
+ SetGreenSample(q,ScaleCharToQuantum(*p++));
+ SetBlueSample(q,ScaleCharToQuantum(*p++));
+ if (image->matte)
+ SetOpacitySample(q,MaxRGB-ScaleCharToQuantum(*p++));
+ else
+ SetOpacitySample(q,OpaqueOpacity);
+ q++;
+ }
- if (!SyncImagePixels(image))
- break;
- }
+ if (!SyncImagePixels(image))
+ break;
+ }
#if defined(SUPPORT_WEBP_MUX)
- /* Read features out of the WebP container */
- {
- uint32_t webp_flags=0;
- WebPData flag_data;
- WebPData content={stream,length};
+ /* Read features out of the WebP container */
+ {
+ uint32_t webp_flags=0;
+ WebPData flag_data;
+ WebPData content={stream,length};
- WebPMux *mux=WebPMuxCreate(&content,0);
- (void) memset(&flag_data,0,sizeof(flag_data));
- WebPMuxGetFeatures(mux,&webp_flags);
+ WebPMux *mux=WebPMuxCreate(&content,0);
+ (void) memset(&flag_data,0,sizeof(flag_data));
+ WebPMuxGetFeatures(mux,&webp_flags);
- if (webp_flags & ICCP_FLAG) {
- WebPMuxGetChunk(mux,"ICCP",&flag_data);
- AppendImageProfile(image,"ICC",flag_data.bytes,flag_data.size);
- }
+ if (webp_flags & ICCP_FLAG) {
+ WebPMuxGetChunk(mux,"ICCP",&flag_data);
+ AppendImageProfile(image,"ICC",flag_data.bytes,flag_data.size);
+ }
- if (webp_flags & EXIF_FLAG) {
- WebPMuxGetChunk(mux,"EXIF",&flag_data);
- AppendImageProfile(image,"EXIF",flag_data.bytes,flag_data.size);
- }
+ if (webp_flags & EXIF_FLAG) {
+ WebPMuxGetChunk(mux,"EXIF",&flag_data);
+ AppendImageProfile(image,"EXIF",flag_data.bytes,flag_data.size);
+ }
- if (webp_flags & XMP_FLAG) {
- WebPMuxGetChunk(mux,"XMP",&flag_data);
- AppendImageProfile(image,"XMP",flag_data.bytes,flag_data.size);
+ if (webp_flags & XMP_FLAG) {
+ WebPMuxGetChunk(mux,"XMP",&flag_data);
+ AppendImageProfile(image,"XMP",flag_data.bytes,flag_data.size);
+ }
+
+ WebPMuxDelete(mux);
}
+#endif /* defined(SUPPORT_WEBP_MUX) */
- WebPMuxDelete(mux);
+ /*
+ Free scale resource.
+ */
+ free(pixels);
+ pixels=(unsigned char *) NULL;
}
-#endif /* defined(SUPPORT_WEBP_MUX) */
- /*
- Free scale resource.
- */
- free(pixels);
- pixels=(unsigned char *) NULL;
MagickFreeMemory(stream);
CloseBlob(image);
return(image);
diff --git a/configure b/configure
index 9581a4e..83d410b 100755
--- a/configure
+++ b/configure
@@ -27510,7 +27510,7 @@ if ${ac_cv_lib_webp_WebPDecodeRGB+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
-LIBS="-lwebp $LIBS"
+LIBS="-lwebp -lwebpdemux $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -27541,7 +27541,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_webp_WebPDecodeRGB" >&5
$as_echo "$ac_cv_lib_webp_WebPDecodeRGB" >&6; }
if test "x$ac_cv_lib_webp_WebPDecodeRGB" = xyes; then :
- passed=`expr $passed + 1`; LIB_WEBP='-lwebp'
+ passed=`expr $passed + 1`; LIB_WEBP='-lwebp -lwebpdemux'
else
failed=`expr $failed + 1`
fi
@@ -27552,7 +27552,7 @@ if ${ac_cv_lib_webpmux_WebPMuxSetImage+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
-LIBS="-lwebpmux -lwebp $LIBS"
+LIBS="-lwebpmux -lwebp -lwebpdemux $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
diff --git a/configure.ac b/configure.ac
index 6d77a20..0776701 100755
--- a/configure.ac
+++ b/configure.ac
@@ -2534,6 +2534,7 @@ then
AC_CHECK_HEADER([webp/encode.h],[passed=`expr $passed + 1`],[failed=`expr $failed + 1`],[])
AC_CHECK_LIB([webp],[WebPDecodeRGB],[passed=`expr $passed + 1`; LIB_WEBP='-lwebp'],[failed=`expr $failed + 1`],[])
AC_CHECK_LIB([webpmux],[WebPMuxSetImage],[LIB_WEBP="$LIB_WEBP -lwebpmux"],[],[-lwebp])
+ AC_CHECK_LIB([webpdemux],[WebPDeMuxGetFrame],[LIB_WEBP="$LIB_WEBP -lwebpdemux"],[],[-lwebp])
AC_MSG_CHECKING(if WEBP package is complete)
if test $passed -gt 0
then
diff --git a/packaging/GraphicsMagick.spec b/packaging/GraphicsMagick.spec
index 519da7c..402f1f9 100755
--- a/packaging/GraphicsMagick.spec
+++ b/packaging/GraphicsMagick.spec
@@ -14,7 +14,6 @@ BuildRequires: libpng-devel
BuildRequires: libtool-ltdl-devel
BuildRequires: libxml2-devel
BuildRequires: pkgconfig(libwebp)
-BuildRequires: pkgconfig(libwebpdecoder)
BuildRequires: pkgconfig(libwebpdemux)
BuildRequires: pkgconfig(libwebpmux)
BuildRequires: xz-devel