summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjiyong.min <jiyong.min@samsung.com>2020-07-10 13:31:36 +0900
committerbackto.kim <backto.kim@samsung.com>2021-12-07 10:34:28 +0900
commitfde3d2c67bd7f9c4d2e993cd619febe13c8895ab (patch)
tree04715e81944fbbcb37cfb32b3e1f4215bfecf846
parentc0c0ebec79fe3e52313539f42c9d93bfae86b401 (diff)
downloadGraphicsMagick-fde3d2c67bd7f9c4d2e993cd619febe13c8895ab.tar.gz
GraphicsMagick-fde3d2c67bd7f9c4d2e993cd619febe13c8895ab.tar.bz2
GraphicsMagick-fde3d2c67bd7f9c4d2e993cd619febe13c8895ab.zip
Apply animated WebP patch for WebP coder
- WebP is based on RIFF container and it provides additional container for animation. If a WebP image contains animation container, image data is included in the animation container. But GraphicsMagick does not support animation container. For the reason, GraphicsMagick return invalid image for animated WebP. (The mandatory image data is not found) So we add to parse animation container and decode 1 frame. - Tizen platform supports 1 frame for animated image format. Change-Id: I1894517769d1b097f7103fe55a525211e7519589
-rw-r--r--coders/webp.c378
-rwxr-xr-xconfigure6
-rw-r--r--configure.ac1
-rwxr-xr-xpackaging/GraphicsMagick.spec1
4 files changed, 328 insertions, 58 deletions
diff --git a/coders/webp.c b/coders/webp.c
index 54eb608..9529b4e 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,
MagickFreeResourceLimitedMemory(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)
{
MagickFreeResourceLimitedMemory(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;
@@ -260,85 +521,94 @@ static Image *ReadWEBPImage(const ImageInfo *image_info,
MagickFreeResourceLimitedMemory(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)
- {
- MagickFreeResourceLimitedMemory(stream);
- ThrowReaderException(CoderError,NoDataReturned,image);
- }
+ if (pixels == (unsigned char *) NULL)
+ {
+ MagickFreeResourceLimitedMemory(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
- https://developers.google.com/speed/webp/docs/container-api
- */
- {
- uint32_t webp_flags=0;
- WebPData flag_data;
- WebPData content={stream,length};
+ /*
+ Read features out of the WebP container
+ https://developers.google.com/speed/webp/docs/container-api
+ */
+ {
+ 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)
- {
+ if (webp_flags & ICCP_FLAG) {
WebPMuxGetChunk(mux,"ICCP",&flag_data);
SetImageProfile(image,"ICC",flag_data.bytes,flag_data.size);
}
- if (webp_flags & EXIF_FLAG)
- {
+ if (webp_flags & EXIF_FLAG) {
WebPMuxGetChunk(mux,"EXIF",&flag_data);
SetImageProfile(image,"EXIF",flag_data.bytes,flag_data.size);
}
- if (webp_flags & XMP_FLAG)
- {
+ if (webp_flags & XMP_FLAG) {
WebPMuxGetChunk(mux,"XMP",&flag_data);
SetImageProfile(image,"XMP",flag_data.bytes,flag_data.size);
}
- WebPMuxDelete(mux);
- }
+ WebPMuxDelete(mux);
+ }
#endif /* defined(SUPPORT_WEBP_MUX) */
- /*
- Free scale resource.
- */
- free(pixels);
- pixels=(unsigned char *) NULL;
+ /*
+ Free scale resource.
+ */
+ free(pixels);
+ pixels=(unsigned char *) NULL;
+ }
+
MagickFreeResourceLimitedMemory(stream);
CloseBlob(image);
StopTimer(&image->timer);
diff --git a/configure b/configure
index b2f0342..f981146 100755
--- a/configure
+++ b/configure
@@ -27061,7 +27061,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. */
@@ -27092,7 +27092,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
@@ -27103,7 +27103,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 d6a30f6..aa20ab9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2511,6 +2511,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