mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 02:15:42 +01:00 
			
		
		
		
	Merge "migrate anim_diff tool from C++ to C89"
This commit is contained in:
		
							
								
								
									
										11
									
								
								Makefile.vc
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Makefile.vc
									
									
									
									
									
								
							| @@ -325,6 +325,7 @@ gif2webp: $(DIRBIN)\gif2webp.exe | |||||||
| anim_diff: $(DIRBIN)\anim_diff.exe | anim_diff: $(DIRBIN)\anim_diff.exe | ||||||
|  |  | ||||||
| $(DIRBIN)\anim_diff.exe: $(DIROBJ)\examples\anim_diff.obj $(EX_ANIM_UTIL_OBJS) | $(DIRBIN)\anim_diff.exe: $(DIROBJ)\examples\anim_diff.obj $(EX_ANIM_UTIL_OBJS) | ||||||
|  | $(DIRBIN)\anim_diff.exe: $(EX_UTIL_OBJS) | ||||||
| $(DIRBIN)\anim_diff.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP) | $(DIRBIN)\anim_diff.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP) | ||||||
| $(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(EX_FORMAT_DEC_OBJS) | $(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(EX_FORMAT_DEC_OBJS) | ||||||
| $(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj | $(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj | ||||||
| @@ -382,18 +383,18 @@ $(DIROBJ)\$(DLLINC): | |||||||
| 	@echo #define WEBP_EXTERN(type) __declspec(dllexport) type >> $@ | 	@echo #define WEBP_EXTERN(type) __declspec(dllexport) type >> $@ | ||||||
| 	@echo #endif  /* WEBP_DLL_H_ */ >> $@ | 	@echo #endif  /* WEBP_DLL_H_ */ >> $@ | ||||||
|  |  | ||||||
| .SUFFIXES: .c .cc .obj .res .exe | .SUFFIXES: .c .obj .res .exe | ||||||
| # File-specific flag builds. Note batch rules take precedence over wildcards, | # File-specific flag builds. Note batch rules take precedence over wildcards, | ||||||
| # so for now name each file individually. | # so for now name each file individually. | ||||||
| $(DIROBJ)\dsp\enc_avx2.obj: src\dsp\enc_avx2.c | $(DIROBJ)\dsp\enc_avx2.obj: src\dsp\enc_avx2.c | ||||||
| 	$(CC) $(CFLAGS) $(AVX2_FLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ \ | 	$(CC) $(CFLAGS) $(AVX2_FLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ \ | ||||||
| 	  src\dsp\$(@B).c | 	  src\dsp\$(@B).c | ||||||
| $(DIROBJ)\examples\anim_diff.obj: examples\anim_diff.cc | $(DIROBJ)\examples\anim_diff.obj: examples\anim_diff.c | ||||||
| 	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ | 	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ | ||||||
| 	  /Fo$(DIROBJ)\examples\ examples\$(@B).cc | 	  /Fo$(DIROBJ)\examples\ examples\$(@B).c | ||||||
| $(DIROBJ)\examples\anim_util.obj: examples\anim_util.cc | $(DIROBJ)\examples\anim_util.obj: examples\anim_util.c | ||||||
| 	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ | 	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ | ||||||
| 	  /Fo$(DIROBJ)\examples\ examples\$(@B).cc | 	  /Fo$(DIROBJ)\examples\ examples\$(@B).c | ||||||
| $(DIROBJ)\examples\gif2webp.obj: examples\gif2webp.c | $(DIROBJ)\examples\gif2webp.obj: examples\gif2webp.c | ||||||
| 	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ | 	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ | ||||||
| 	  /Fo$(DIROBJ)\examples\ examples\$(@B).c | 	  /Fo$(DIROBJ)\examples\ examples\$(@B).c | ||||||
|   | |||||||
							
								
								
									
										84
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -11,7 +11,6 @@ m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) | |||||||
|  |  | ||||||
| AC_PROG_LIBTOOL | AC_PROG_LIBTOOL | ||||||
| AM_PROG_CC_C_O | AM_PROG_CC_C_O | ||||||
| AC_PROG_CXX |  | ||||||
|  |  | ||||||
| dnl === Enable less verbose output when building. | dnl === Enable less verbose output when building. | ||||||
| m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) | ||||||
| @@ -37,44 +36,34 @@ AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=DIR], | |||||||
|             [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) |             [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) | ||||||
| AC_SUBST([pkgconfigdir]) | AC_SUBST([pkgconfigdir]) | ||||||
|  |  | ||||||
| dnl === TEST_AND_ADD_FLAGS(var, compiler, flag) | dnl === TEST_AND_ADD_CFLAGS(var, flag) | ||||||
| dnl ===   Checks whether $CC (or $CXX if compiler is 'CXX') supports 'flag' and | dnl ===   Checks whether $CC supports 'flag' and adds it to 'var' | ||||||
| dnl ===   adds it to 'var' on success. | dnl ===   on success. | ||||||
| AC_DEFUN([TEST_AND_ADD_FLAGS], | AC_DEFUN([TEST_AND_ADD_CFLAGS], | ||||||
|          [AS_IF([test "$2" = "CXX"], |          [SAVED_CFLAGS="$CFLAGS" | ||||||
|                 [SAVED_FLAGS="$CXXFLAGS" |           CFLAGS="-Werror $2" | ||||||
|                  CXXFLAGS="-Werror $3" |           AC_MSG_CHECKING([whether $CC supports $2]) | ||||||
|                  AC_MSG_CHECKING([whether $CXX supports $3]) |  | ||||||
|                  AC_LANG_PUSH([C++])], |  | ||||||
|                 [SAVED_FLAGS="$CFLAGS" |  | ||||||
|                  CFLAGS="-Werror $3" |  | ||||||
|                  AC_MSG_CHECKING([whether $CC supports $3])]) |  | ||||||
|           dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition. |           dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition. | ||||||
|           AC_COMPILE_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])], |           AC_COMPILE_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])], | ||||||
|                             [AC_MSG_RESULT([yes])] |                             [AC_MSG_RESULT([yes])] | ||||||
|                             dnl Simply append the variable avoiding a |                             dnl Simply append the variable avoiding a | ||||||
|                             dnl compatibility ifdef for AS_VAR_APPEND as this |                             dnl compatibility ifdef for AS_VAR_APPEND as this | ||||||
|                             dnl variable shouldn't grow all that large. |                             dnl variable shouldn't grow all that large. | ||||||
|                             [$1="${$1} $3"], |                             [$1="${$1} $2"], | ||||||
|                             [AC_MSG_RESULT([no])]) |                             [AC_MSG_RESULT([no])]) | ||||||
|           AS_IF([test "$2" = "CXX"], |           CFLAGS="$SAVED_CFLAGS"]) | ||||||
|                 [AC_LANG_POP([C++]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wall]) | ||||||
|                  CXXFLAGS="$SAVED_FLAGS"], | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wdeclaration-after-statement]) | ||||||
|                 [CFLAGS="$SAVED_FLAGS"])]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wextra]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wall]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat-nonliteral]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wdeclaration-after-statement]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat-security]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wextra]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-declarations]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wformat-nonliteral]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-prototypes]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wformat-security]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wmissing-declarations]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshadow]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wmissing-prototypes]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused-but-set-variable]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wold-style-definition]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wshadow]) | TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wvla]) | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wshorten-64-to-32]) |  | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wunreachable-code]) |  | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wunused-but-set-variable]) |  | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wunused]) |  | ||||||
| TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-Wvla]) |  | ||||||
| # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62040 | # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62040 | ||||||
| # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61622 | # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61622 | ||||||
| AS_IF([test "$GCC" = "yes" ], [ | AS_IF([test "$GCC" = "yes" ], [ | ||||||
| @@ -87,7 +76,7 @@ AS_IF([test "$GCC" = "yes" ], [ | |||||||
|           esac |           esac | ||||||
|        esac |        esac | ||||||
|        AS_IF([test "$gcc_wht_bug" = "yes"], [ |        AS_IF([test "$gcc_wht_bug" = "yes"], [ | ||||||
|               TEST_AND_ADD_FLAGS([AM_CFLAGS], [C], [-frename-registers])])]) |               TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-frename-registers])])]) | ||||||
| AC_SUBST([AM_CFLAGS]) | AC_SUBST([AM_CFLAGS]) | ||||||
|  |  | ||||||
| dnl === Check for machine specific flags | dnl === Check for machine specific flags | ||||||
| @@ -98,7 +87,7 @@ AC_ARG_ENABLE([avx2], | |||||||
|  |  | ||||||
| AS_IF([test "x$enable_avx2" != "xno" -a "x$enable_sse4_1" != "xno" \ | AS_IF([test "x$enable_avx2" != "xno" -a "x$enable_sse4_1" != "xno" \ | ||||||
|          -a "x$enable_sse2" != "xno"], [ |          -a "x$enable_sse2" != "xno"], [ | ||||||
|   TEST_AND_ADD_FLAGS([AVX2_FLAGS], [C], [-mavx2]) |   TEST_AND_ADD_CFLAGS([AVX2_FLAGS], [-mavx2]) | ||||||
|   AS_IF([test -n "$AVX2_FLAGS"], [ |   AS_IF([test -n "$AVX2_FLAGS"], [ | ||||||
|     SAVED_CFLAGS=$CFLAGS |     SAVED_CFLAGS=$CFLAGS | ||||||
|     CFLAGS="$CFLAGS $AVX2_FLAGS" |     CFLAGS="$CFLAGS $AVX2_FLAGS" | ||||||
| @@ -121,7 +110,7 @@ AC_ARG_ENABLE([sse4.1], | |||||||
|                               @<:@default=auto@:>@])) |                               @<:@default=auto@:>@])) | ||||||
|  |  | ||||||
| AS_IF([test "x$enable_sse4_1" != "xno" -a "x$enable_sse2" != "xno"], [ | AS_IF([test "x$enable_sse4_1" != "xno" -a "x$enable_sse2" != "xno"], [ | ||||||
|   TEST_AND_ADD_FLAGS([SSE41_FLAGS], [C], [-msse4.1]) |   TEST_AND_ADD_CFLAGS([SSE41_FLAGS], [-msse4.1]) | ||||||
|   AS_IF([test -n "$SSE41_FLAGS"], [ |   AS_IF([test -n "$SSE41_FLAGS"], [ | ||||||
|     SAVED_CFLAGS=$CFLAGS |     SAVED_CFLAGS=$CFLAGS | ||||||
|     CFLAGS="$CFLAGS $SSE41_FLAGS" |     CFLAGS="$CFLAGS $SSE41_FLAGS" | ||||||
| @@ -138,7 +127,7 @@ AC_ARG_ENABLE([sse2], | |||||||
|                               @<:@default=auto@:>@])) |                               @<:@default=auto@:>@])) | ||||||
|  |  | ||||||
| AS_IF([test "x$enable_sse2" != "xno"], [ | AS_IF([test "x$enable_sse2" != "xno"], [ | ||||||
|   TEST_AND_ADD_FLAGS([SSE2_FLAGS], [C], [-msse2]) |   TEST_AND_ADD_CFLAGS([SSE2_FLAGS], [-msse2]) | ||||||
|   AS_IF([test -n "$SSE2_FLAGS"], [ |   AS_IF([test -n "$SSE2_FLAGS"], [ | ||||||
|     SAVED_CFLAGS=$CFLAGS |     SAVED_CFLAGS=$CFLAGS | ||||||
|     CFLAGS="$CFLAGS $SSE2_FLAGS" |     CFLAGS="$CFLAGS $SSE2_FLAGS" | ||||||
| @@ -449,30 +438,7 @@ AS_IF([test "x$enable_gif" != "xno"], [ | |||||||
|  |  | ||||||
|   if test "$gif_support" = "yes" -a \ |   if test "$gif_support" = "yes" -a \ | ||||||
|           "$enable_libwebpdemux" = "yes"; then |           "$enable_libwebpdemux" = "yes"; then | ||||||
|     dnl === Enable C++ compiler for .cc sources, if available. |  | ||||||
|     AC_LANG_PUSH([C++]) |  | ||||||
|     AC_LINK_IFELSE( |  | ||||||
|       [AC_LANG_PROGRAM([#include <iostream>], |  | ||||||
|                        [std::cout << "a";])], |  | ||||||
|       [cxx_support=yes], |  | ||||||
|       [AC_MSG_WARN([C++ compiler absent: compilation of C++ sources disabled.]) |  | ||||||
|        cxx_support=no]) |  | ||||||
|     AC_LANG_POP([C++]) |  | ||||||
|     if test "$cxx_support" = "yes"; then |  | ||||||
|     build_animdiff=yes |     build_animdiff=yes | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wall]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wextra]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wformat-nonliteral]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wformat-security]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wmissing-declarations]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wshadow]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wshorten-64-to-32]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wunreachable-code]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wunused-but-set-variable]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wunused]) |  | ||||||
|       TEST_AND_ADD_FLAGS([AM_CXXFLAGS], [CXX], [-Wvla]) |  | ||||||
|       AC_SUBST([AM_CXXFLAGS]) |  | ||||||
|     fi |  | ||||||
|   fi |   fi | ||||||
|  |  | ||||||
|   if test "$gif_support" = "yes" -a \ |   if test "$gif_support" = "yes" -a \ | ||||||
|   | |||||||
| @@ -20,10 +20,11 @@ if BUILD_ANIMDIFF | |||||||
|   noinst_PROGRAMS = anim_diff |   noinst_PROGRAMS = anim_diff | ||||||
| endif | endif | ||||||
|  |  | ||||||
| anim_diff_SOURCES = anim_diff.cc anim_util.cc anim_util.h | anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h | ||||||
| anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES) | anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES) | ||||||
| anim_diff_LDADD  = ../src/demux/libwebpdemux.la | anim_diff_LDADD  = ../src/demux/libwebpdemux.la | ||||||
| anim_diff_LDADD += $(GIF_LIBS) | anim_diff_LDADD += libexampleutil.la | ||||||
|  | anim_diff_LDADD += $(GIF_LIBS) -lm | ||||||
|  |  | ||||||
| dwebp_SOURCES = dwebp.c stopwatch.h | dwebp_SOURCES = dwebp.c stopwatch.h | ||||||
| dwebp_CPPFLAGS  = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) | dwebp_CPPFLAGS  = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) | ||||||
|   | |||||||
| @@ -18,129 +18,136 @@ | |||||||
| #include <stdlib.h>  // for 'strtod'. | #include <stdlib.h>  // for 'strtod'. | ||||||
| #include <string.h>  // for 'strcmp'. | #include <string.h>  // for 'strcmp'. | ||||||
| 
 | 
 | ||||||
| #include <iostream>  // for 'cout'. |  | ||||||
| #include <sstream>   // for 'ostringstream'. |  | ||||||
| 
 |  | ||||||
| #include "./anim_util.h" | #include "./anim_util.h" | ||||||
| 
 | 
 | ||||||
| namespace { | #if defined(_MSC_VER) && _MSC_VER < 1900 | ||||||
|  | #define snprintf _snprintf | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| // Return true if 'a + b' will overflow.
 | // Returns true if 'a + b' will overflow.
 | ||||||
| bool AdditionWillOverflow(int a, int b) { | static int AdditionWillOverflow(int a, int b) { | ||||||
|   return (b > 0) && (a > INT_MAX - b); |   return (b > 0) && (a > INT_MAX - b); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Minimize number of frames by combining successive frames that have exact same
 | // Minimize number of frames by combining successive frames that have exact same
 | ||||||
| // ARGB data into a single longer duration frame.
 | // ARGB data into a single longer duration frame.
 | ||||||
| void MinimizeAnimationFrames(AnimatedImage* const img) { | static void MinimizeAnimationFrames(AnimatedImage* const img) { | ||||||
|   for (size_t i = 1; i < img->frames.size(); ++i) { |   uint32_t i; | ||||||
|  |   for (i = 1; i < img->num_frames; ++i) { | ||||||
|     DecodedFrame* const frame1 = &img->frames[i - 1]; |     DecodedFrame* const frame1 = &img->frames[i - 1]; | ||||||
|     DecodedFrame* const frame2 = &img->frames[i]; |     DecodedFrame* const frame2 = &img->frames[i]; | ||||||
|  |     const uint8_t* const rgba1 = frame1->rgba; | ||||||
|  |     const uint8_t* const rgba2 = frame2->rgba; | ||||||
|     // If merging frames will result in integer overflow for 'duration',
 |     // If merging frames will result in integer overflow for 'duration',
 | ||||||
|     // skip merging.
 |     // skip merging.
 | ||||||
|     if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue; |     if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue; | ||||||
|     const uint8_t* rgba1 = frame1->rgba.data(); |  | ||||||
|     const uint8_t* rgba2 = frame2->rgba.data(); |  | ||||||
|     if (!memcmp(rgba1, rgba2, img->canvas_width * 4 * img->canvas_height)) { |     if (!memcmp(rgba1, rgba2, img->canvas_width * 4 * img->canvas_height)) { | ||||||
|       // Merge 'i+1'th frame into 'i'th frame.
 |       // Merge 'i+1'th frame into 'i'th frame.
 | ||||||
|       frame1->duration += frame2->duration; |       frame1->duration += frame2->duration; | ||||||
|       img->frames.erase(img->frames.begin() + i); |       if (i + 1 < img->num_frames) { | ||||||
|  |         memmove(&img->frames[i], &img->frames[i + 1], | ||||||
|  |                 (img->num_frames - i - 1) * sizeof(*img->frames)); | ||||||
|  |       } | ||||||
|  |       --img->num_frames; | ||||||
|       --i; |       --i; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<typename T> | static int CompareValues(uint32_t a, uint32_t b, const char* output_str) { | ||||||
| bool CompareValues(T a, T b, const std::string& output_str) { |  | ||||||
|   if (a != b) { |   if (a != b) { | ||||||
|     std::cout << output_str << ": " << a << " vs " << b << std::endl; |     fprintf(stderr, "%s: %d vs %d\n", output_str, a, b); | ||||||
|     return false; |     return 0; | ||||||
|   } |   } | ||||||
|   return true; |   return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Note: As long as frame durations and reconstructed frames are identical, it
 | // Note: As long as frame durations and reconstructed frames are identical, it
 | ||||||
| // is OK for other aspects like offsets, dispose/blend method to vary.
 | // is OK for other aspects like offsets, dispose/blend method to vary.
 | ||||||
| bool CompareAnimatedImagePair(const AnimatedImage& img1, | static int CompareAnimatedImagePair(const AnimatedImage* const img1, | ||||||
|                               const AnimatedImage& img2, |                                     const AnimatedImage* const img2, | ||||||
|                               bool premultiply, |                                     int premultiply, | ||||||
|                                     double min_psnr) { |                                     double min_psnr) { | ||||||
|   bool ok = true; |   int ok = 1; | ||||||
|   ok = CompareValues(img1.canvas_width, img2.canvas_width, |   const int is_multi_frame_image = (img1->num_frames > 1); | ||||||
|                      "Canvas width mismatch") && ok; |   uint32_t i; | ||||||
|   ok = CompareValues(img1.canvas_height, img2.canvas_height, | 
 | ||||||
|                      "Canvas height mismatch") && ok; |   ok = CompareValues(img1->canvas_width, img2->canvas_width, | ||||||
|   ok = CompareValues(img1.frames.size(), img2.frames.size(), |                      "Canvas width mismatch") && ok; | ||||||
|                      "Frame count mismatch") && ok; |   ok = CompareValues(img1->canvas_height, img2->canvas_height, | ||||||
|   if (!ok) return false;  // These are fatal failures, can't proceed.
 |                      "Canvas height mismatch") && ok; | ||||||
|  |   ok = CompareValues(img1->num_frames, img2->num_frames, | ||||||
|  |                      "Frame count mismatch") && ok; | ||||||
|  |   if (!ok) return 0;  // These are fatal failures, can't proceed.
 | ||||||
| 
 | 
 | ||||||
|   const bool is_multi_frame_image = (img1.frames.size() > 1); |  | ||||||
|   if (is_multi_frame_image) {  // Checks relevant for multi-frame images only.
 |   if (is_multi_frame_image) {  // Checks relevant for multi-frame images only.
 | ||||||
|     ok = CompareValues(img1.loop_count, img2.loop_count, |     ok = CompareValues(img1->loop_count, img2->loop_count, | ||||||
|                        "Loop count mismatch") && ok; |                        "Loop count mismatch") && ok; | ||||||
|     ok = CompareValues(img1.bgcolor, img2.bgcolor, |     ok = CompareValues(img1->bgcolor, img2->bgcolor, | ||||||
|                        "Background color mismatch") && ok; |                        "Background color mismatch") && ok; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   for (size_t i = 0; i < img1.frames.size(); ++i) { |   for (i = 0; i < img1->num_frames; ++i) { | ||||||
|     if (is_multi_frame_image) {  // Check relevant for multi-frame images only.
 |  | ||||||
|       std::ostringstream error_str; |  | ||||||
|       error_str << "Frame #" << i << ", duration mismatch"; |  | ||||||
|       ok = CompareValues(img1.frames[i].duration, img2.frames[i].duration, |  | ||||||
|                          error_str.str()) && ok; |  | ||||||
|     } |  | ||||||
|     // Pixel-by-pixel comparison.
 |     // Pixel-by-pixel comparison.
 | ||||||
|     const uint8_t* rgba1 = img1.frames[i].rgba.data(); |     const uint8_t* const rgba1 = img1->frames[i].rgba; | ||||||
|     const uint8_t* rgba2 = img2.frames[i].rgba.data(); |     const uint8_t* const rgba2 = img2->frames[i].rgba; | ||||||
|     int max_diff; |     int max_diff; | ||||||
|     double psnr; |     double psnr; | ||||||
|     GetDiffAndPSNR(rgba1, rgba2, img1.canvas_width, img1.canvas_height, |     if (is_multi_frame_image) {  // Check relevant for multi-frame images only.
 | ||||||
|  |       const char format[] = "Frame #%d, duration mismatch"; | ||||||
|  |       char tmp[sizeof(format) + 8]; | ||||||
|  |       ok = ok && (snprintf(tmp, sizeof(tmp), format, i) >= 0); | ||||||
|  |       ok = ok && CompareValues(img1->frames[i].duration, | ||||||
|  |                                img2->frames[i].duration, tmp); | ||||||
|  |     } | ||||||
|  |     GetDiffAndPSNR(rgba1, rgba2, img1->canvas_width, img1->canvas_height, | ||||||
|                    premultiply, &max_diff, &psnr); |                    premultiply, &max_diff, &psnr); | ||||||
|     if (min_psnr > 0.) { |     if (min_psnr > 0.) { | ||||||
|       if (psnr < min_psnr) { |       if (psnr < min_psnr) { | ||||||
|         fprintf(stderr, "Frame #%zu, psnr = %.2lf (min_psnr = %f)\n", i, |         fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i, | ||||||
|                 psnr, min_psnr); |                 psnr, min_psnr); | ||||||
|         ok = false; |         ok = 0; | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       if (max_diff != 0) { |       if (max_diff != 0) { | ||||||
|         fprintf(stderr, "Frame #%zu, max pixel diff: %d\n", i, max_diff); |         fprintf(stderr, "Frame #%d, max pixel diff: %d\n", i, max_diff); | ||||||
|         ok = false; |         ok = 0; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   return ok; |   return ok; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Help() { | static void Help(void) { | ||||||
|   printf("\nUsage: anim_diff <image1> <image2> [-dump_frames <folder>] " |   printf("\nUsage: anim_diff <image1> <image2> [-dump_frames <folder>] " | ||||||
|          "[-min_psnr <float>][-raw_comparison]\n"); |          "[-min_psnr <float>][-raw_comparison]\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| }  // namespace
 |  | ||||||
| 
 |  | ||||||
| int main(int argc, const char* argv[]) { | int main(int argc, const char* argv[]) { | ||||||
|   bool dump_frames = false; |   int return_code = -1; | ||||||
|  |   int dump_frames = 0; | ||||||
|   const char* dump_folder = NULL; |   const char* dump_folder = NULL; | ||||||
|   double min_psnr = 0.; |   double min_psnr = 0.; | ||||||
|   bool got_input1 = false; |   int got_input1 = 0; | ||||||
|   bool got_input2 = false; |   int got_input2 = 0; | ||||||
|   bool premultiply = true; |   int premultiply = 1; | ||||||
|   const char* files[2]; |   int i, c; | ||||||
|  |   const char* files[2] = { NULL, NULL }; | ||||||
|  |   AnimatedImage images[2]; | ||||||
| 
 | 
 | ||||||
|   if (argc < 3) { |   if (argc < 3) { | ||||||
|     Help(); |     Help(); | ||||||
|     return -1; |     return -1; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   for (int c = 1; c < argc; ++c) { |   for (c = 1; c < argc; ++c) { | ||||||
|     bool parse_error = false; |     int parse_error = 0; | ||||||
|     if (!strcmp(argv[c], "-dump_frames")) { |     if (!strcmp(argv[c], "-dump_frames")) { | ||||||
|       if (c < argc - 1) { |       if (c < argc - 1) { | ||||||
|         dump_frames = true; |         dump_frames = 1; | ||||||
|         dump_folder = argv[++c]; |         dump_folder = argv[++c]; | ||||||
|       } else { |       } else { | ||||||
|         parse_error = true; |         parse_error = 1; | ||||||
|       } |       } | ||||||
|     } else if (!strcmp(argv[c], "-min_psnr")) { |     } else if (!strcmp(argv[c], "-min_psnr")) { | ||||||
|       if (c < argc - 1) { |       if (c < argc - 1) { | ||||||
| @@ -148,24 +155,24 @@ int main(int argc, const char* argv[]) { | |||||||
|         char* end = NULL; |         char* end = NULL; | ||||||
|         const double d = strtod(v, &end); |         const double d = strtod(v, &end); | ||||||
|         if (end == v) { |         if (end == v) { | ||||||
|           parse_error = true; |           parse_error = 1; | ||||||
|           fprintf(stderr, "Error! '%s' is not a floating point number.\n", v); |           fprintf(stderr, "Error! '%s' is not a floating point number.\n", v); | ||||||
|         } |         } | ||||||
|         min_psnr = d; |         min_psnr = d; | ||||||
|       } else { |       } else { | ||||||
|         parse_error = true; |         parse_error = 1; | ||||||
|       } |       } | ||||||
|     } else if (!strcmp(argv[c], "-raw_comparison")) { |     } else if (!strcmp(argv[c], "-raw_comparison")) { | ||||||
|       premultiply = false; |       premultiply = 0; | ||||||
|     } else { |     } else { | ||||||
|       if (!got_input1) { |       if (!got_input1) { | ||||||
|         files[0] = argv[c]; |         files[0] = argv[c]; | ||||||
|         got_input1 = true; |         got_input1 = 1; | ||||||
|       } else if (!got_input2) { |       } else if (!got_input2) { | ||||||
|         files[1] = argv[c]; |         files[1] = argv[c]; | ||||||
|         got_input2 = true; |         got_input2 = 1; | ||||||
|       } else { |       } else { | ||||||
|         parse_error = true; |         parse_error = 1; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     if (parse_error) { |     if (parse_error) { | ||||||
| @@ -182,22 +189,28 @@ int main(int argc, const char* argv[]) { | |||||||
|     printf("Dumping decoded frames in: %s\n", dump_folder); |     printf("Dumping decoded frames in: %s\n", dump_folder); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   AnimatedImage images[2]; |   memset(images, 0, sizeof(images)); | ||||||
|   for (int i = 0; i < 2; ++i) { |   for (i = 0; i < 2; ++i) { | ||||||
|     printf("Decoding file: %s\n", files[i]); |     printf("Decoding file: %s\n", files[i]); | ||||||
|     if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) { |     if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) { | ||||||
|       fprintf(stderr, "Error decoding file: %s\n Aborting.\n", files[i]); |       fprintf(stderr, "Error decoding file: %s\n Aborting.\n", files[i]); | ||||||
|       return -2; |       return_code = -2; | ||||||
|     } |       goto End; | ||||||
|  |     } else { | ||||||
|       MinimizeAnimationFrames(&images[i]); |       MinimizeAnimationFrames(&images[i]); | ||||||
|     } |     } | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   if (!CompareAnimatedImagePair(images[0], images[1], |   if (!CompareAnimatedImagePair(&images[0], &images[1], | ||||||
|                                 premultiply, min_psnr)) { |                                 premultiply, min_psnr)) { | ||||||
|     fprintf(stderr, "\nFiles %s and %s differ.\n", files[0], files[1]); |     fprintf(stderr, "\nFiles %s and %s differ.\n", files[0], files[1]); | ||||||
|     return -3; |     return_code = -3; | ||||||
|   } |   } else { | ||||||
| 
 |  | ||||||
|     printf("\nFiles %s and %s are identical.\n", files[0], files[1]); |     printf("\nFiles %s and %s are identical.\n", files[0], files[1]); | ||||||
|   return 0; |     return_code = 0; | ||||||
|  |   } | ||||||
|  |  End: | ||||||
|  |   ClearAnimatedImage(&images[0]); | ||||||
|  |   ClearAnimatedImage(&images[1]); | ||||||
|  |   return return_code; | ||||||
| } | } | ||||||
| @@ -16,20 +16,17 @@ | |||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| 
 | 
 | ||||||
| #include <fstream> |  | ||||||
| #include <sstream>   // for 'ostringstream'. |  | ||||||
| 
 |  | ||||||
| #ifdef WEBP_HAVE_GIF | #ifdef WEBP_HAVE_GIF | ||||||
| #include <gif_lib.h> | #include <gif_lib.h> | ||||||
| #endif | #endif | ||||||
| #include "webp/format_constants.h" | #include "webp/format_constants.h" | ||||||
| #include "webp/decode.h" | #include "webp/decode.h" | ||||||
| #include "webp/demux.h" | #include "webp/demux.h" | ||||||
|  | #include "./example_util.h" | ||||||
| 
 | 
 | ||||||
| using std::ifstream; | #if defined(_MSC_VER) && _MSC_VER < 1900 | ||||||
| using std::ios; | #define snprintf _snprintf | ||||||
| using std::ofstream; | #endif | ||||||
| using std::ostringstream; |  | ||||||
| 
 | 
 | ||||||
| static const int kNumChannels = 4; | static const int kNumChannels = 4; | ||||||
| 
 | 
 | ||||||
| @@ -37,17 +34,43 @@ static const int kNumChannels = 4; | |||||||
| // Common utilities.
 | // Common utilities.
 | ||||||
| 
 | 
 | ||||||
| // Returns true if the frame covers the full canvas.
 | // Returns true if the frame covers the full canvas.
 | ||||||
| static bool IsFullFrame(int width, int height, | static int IsFullFrame(int width, int height, | ||||||
|                        int canvas_width, int canvas_height) { |                        int canvas_width, int canvas_height) { | ||||||
|   return (width == canvas_width && height == canvas_height); |   return (width == canvas_width && height == canvas_height); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void AllocateFrames(AnimatedImage* const image, uint32_t frame_count) { | static int AllocateFrames(AnimatedImage* const image, uint32_t num_frames) { | ||||||
|   image->frames.resize(frame_count); |   uint32_t i; | ||||||
|   for (size_t i = 0; i < image->frames.size(); ++i) { |  | ||||||
|   const size_t rgba_size = |   const size_t rgba_size = | ||||||
|       image->canvas_width * kNumChannels * image->canvas_height; |       image->canvas_width * kNumChannels * image->canvas_height; | ||||||
|     image->frames[i].rgba.resize(rgba_size); |   uint8_t* const mem = (uint8_t*)malloc(num_frames * rgba_size * sizeof(*mem)); | ||||||
|  |   DecodedFrame* const frames = | ||||||
|  |       (DecodedFrame*)malloc(num_frames * sizeof(*frames)); | ||||||
|  | 
 | ||||||
|  |   if (mem == NULL || frames == NULL) { | ||||||
|  |     free(mem); | ||||||
|  |     free(frames); | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |   free(image->raw_mem); | ||||||
|  |   image->num_frames = num_frames; | ||||||
|  |   image->frames = frames; | ||||||
|  |   for (i = 0; i < num_frames; ++i) { | ||||||
|  |     frames[i].rgba = mem + i * rgba_size; | ||||||
|  |     frames[i].duration = 0; | ||||||
|  |     frames[i].is_key_frame = 0; | ||||||
|  |   } | ||||||
|  |   image->raw_mem = mem; | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ClearAnimatedImage(AnimatedImage* const image) { | ||||||
|  |   if (image != NULL) { | ||||||
|  |     free(image->raw_mem); | ||||||
|  |     free(image->frames); | ||||||
|  |     image->num_frames = 0; | ||||||
|  |     image->frames = NULL; | ||||||
|  |     image->raw_mem = NULL; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -60,9 +83,10 @@ static void ZeroFillCanvas(uint8_t* rgba, | |||||||
| // Clear given frame rectangle to transparent.
 | // Clear given frame rectangle to transparent.
 | ||||||
| static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset, | static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset, | ||||||
|                               int y_offset, int width, int height) { |                               int y_offset, int width, int height) { | ||||||
|  |   int j; | ||||||
|   assert(width * kNumChannels <= rgba_stride); |   assert(width * kNumChannels <= rgba_stride); | ||||||
|   rgba += y_offset * rgba_stride + x_offset * kNumChannels; |   rgba += y_offset * rgba_stride + x_offset * kNumChannels; | ||||||
|   for (int j = 0; j < height; ++j) { |   for (j = 0; j < height; ++j) { | ||||||
|     memset(rgba, 0, width * kNumChannels); |     memset(rgba, 0, width * kNumChannels); | ||||||
|     rgba += rgba_stride; |     rgba += rgba_stride; | ||||||
|   } |   } | ||||||
| @@ -79,12 +103,13 @@ static void CopyCanvas(const uint8_t* src, uint8_t* dst, | |||||||
| static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride, | static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride, | ||||||
|                                int x_offset, int y_offset, |                                int x_offset, int y_offset, | ||||||
|                                int width, int height) { |                                int width, int height) { | ||||||
|  |   int j; | ||||||
|   const int width_in_bytes = width * kNumChannels; |   const int width_in_bytes = width * kNumChannels; | ||||||
|   assert(width_in_bytes <= stride); |  | ||||||
|   const size_t offset = y_offset * stride + x_offset * kNumChannels; |   const size_t offset = y_offset * stride + x_offset * kNumChannels; | ||||||
|  |   assert(width_in_bytes <= stride); | ||||||
|   src += offset; |   src += offset; | ||||||
|   dst += offset; |   dst += offset; | ||||||
|   for (int j = 0; j < height; ++j) { |   for (j = 0; j < height; ++j) { | ||||||
|     memcpy(dst, src, width_in_bytes); |     memcpy(dst, src, width_in_bytes); | ||||||
|     src += stride; |     src += stride; | ||||||
|     dst += stride; |     dst += stride; | ||||||
| @@ -104,71 +129,84 @@ static void CleanupTransparentPixels(uint32_t* rgba, | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Dump frame to a PAM file.
 | // Dump frame to a PAM file. Returns true on success.
 | ||||||
| // Returns true on success.
 | static int DumpFrame(const char filename[], const char dump_folder[], | ||||||
| static bool DumpFrame(const char filename[], const char dump_folder[], |  | ||||||
|                      uint32_t frame_num, const uint8_t rgba[], |                      uint32_t frame_num, const uint8_t rgba[], | ||||||
|                      int canvas_width, int canvas_height) { |                      int canvas_width, int canvas_height) { | ||||||
|   const std::string filename_str = filename; |   int ok = 0; | ||||||
|   const size_t slash_idx = filename_str.find_last_of("/\\"); |   size_t max_len; | ||||||
|   const std::string base_name = (slash_idx != std::string::npos) |   int y; | ||||||
|                                     ? filename_str.substr(slash_idx + 1) |   const char* base_name = NULL; | ||||||
|                                     : filename_str; |   char* file_name = NULL; | ||||||
|   ostringstream dump_file; |   FILE* f = NULL; | ||||||
|   dump_file << dump_folder << "/" << base_name << "_frame_" << frame_num |  | ||||||
|             << ".pam"; |  | ||||||
| 
 | 
 | ||||||
|   ofstream fout(dump_file.str().c_str(), ios::binary | ios::out); |   base_name = strrchr(filename, '/'); | ||||||
|   if (!fout.good()) { |   base_name = (base_name == NULL) ? filename : base_name + 1; | ||||||
|     fprintf(stderr, "Error opening file for writing: %s\n", |   max_len = strlen(dump_folder) + 1 + strlen(base_name) | ||||||
|             dump_file.str().c_str()); |           + strlen("_frame_") + strlen(".pam") + 8; | ||||||
|     return false; |   file_name = (char*)malloc(max_len * sizeof(*file_name)); | ||||||
|  |   if (file_name == NULL) goto End; | ||||||
|  | 
 | ||||||
|  |   if (snprintf(file_name, max_len, "%s/%s_frame_%d.pam", | ||||||
|  |                dump_folder, base_name, frame_num) < 0) { | ||||||
|  |     fprintf(stderr, "Error while generating file name\n"); | ||||||
|  |     goto End; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fout << "P7\nWIDTH " << canvas_width << "\nHEIGHT " << canvas_height |   f = fopen(file_name, "wb"); | ||||||
|        << "\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n"; |   if (f == NULL) { | ||||||
|   for (int y = 0; y < canvas_height; ++y) { |     fprintf(stderr, "Error opening file for writing: %s\n", file_name); | ||||||
|     fout.write( |     ok = 0; | ||||||
|         reinterpret_cast<const char*>(rgba) + y * canvas_width * kNumChannels, |     goto End; | ||||||
|         canvas_width * kNumChannels); |   } | ||||||
|     if (!fout.good()) { |   if (fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\n" | ||||||
|       fprintf(stderr, "Error writing to file: %s\n", dump_file.str().c_str()); |               "DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n", | ||||||
|       return 0; |               canvas_width, canvas_height) < 0) { | ||||||
|  |     fprintf(stderr, "Write error for file %s\n", file_name); | ||||||
|  |     goto End; | ||||||
|  |   } | ||||||
|  |   for (y = 0; y < canvas_height; ++y) { | ||||||
|  |     if (fwrite((const char*)(rgba) + y * canvas_width * kNumChannels, | ||||||
|  |                canvas_width * kNumChannels, 1, f) != 1) { | ||||||
|  |       fprintf(stderr, "Error writing to file: %s\n", file_name); | ||||||
|  |       goto End; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   fout.close(); |   ok = 1; | ||||||
|   return true; |  End: | ||||||
|  |   if (f != NULL) fclose(f); | ||||||
|  |   free(file_name); | ||||||
|  |   return ok; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // -----------------------------------------------------------------------------
 | // -----------------------------------------------------------------------------
 | ||||||
| // WebP Decoding.
 | // WebP Decoding.
 | ||||||
| 
 | 
 | ||||||
| // Returns true if this is a valid WebP bitstream.
 | // Returns true if this is a valid WebP bitstream.
 | ||||||
| static bool IsWebP(const std::string& file_str) { | static int IsWebP(const WebPData* const webp_data) { | ||||||
|   return WebPGetInfo(reinterpret_cast<const uint8_t*>(file_str.c_str()), |   return (WebPGetInfo(webp_data->bytes, webp_data->size, NULL, NULL) != 0); | ||||||
|                      file_str.length(), NULL, NULL) != 0; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Read animated WebP bitstream 'file_str' into 'AnimatedImage' struct.
 | // Read animated WebP bitstream 'file_str' into 'AnimatedImage' struct.
 | ||||||
| static bool ReadAnimatedWebP(const char filename[], const std::string& file_str, | static int ReadAnimatedWebP(const char filename[], | ||||||
|                              AnimatedImage* const image, bool dump_frames, |                             const WebPData* const webp_data, | ||||||
|  |                             AnimatedImage* const image, int dump_frames, | ||||||
|                             const char dump_folder[]) { |                             const char dump_folder[]) { | ||||||
|   bool ok = false; |   int ok = 0; | ||||||
|   bool dump_ok = true; |   int dump_ok = 1; | ||||||
|   uint32_t frame_index = 0; |   uint32_t frame_index = 0; | ||||||
|   int prev_frame_timestamp = 0; |   int prev_frame_timestamp = 0; | ||||||
|  |   WebPAnimDecoder* dec; | ||||||
|  |   WebPAnimInfo anim_info; | ||||||
| 
 | 
 | ||||||
|   const WebPData webp_data = { |   memset(image, 0, sizeof(*image)); | ||||||
|     reinterpret_cast<const uint8_t*>(file_str.data()), file_str.size() |  | ||||||
|   }; |  | ||||||
| 
 | 
 | ||||||
|   WebPAnimDecoder* dec = WebPAnimDecoderNew(&webp_data); |   dec = WebPAnimDecoderNew(webp_data); | ||||||
|   if (dec == NULL) { |   if (dec == NULL) { | ||||||
|     fprintf(stderr, "Error parsing image: %s\n", filename); |     fprintf(stderr, "Error parsing image: %s\n", filename); | ||||||
|     goto End; |     goto End; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   WebPAnimInfo anim_info; |  | ||||||
|   if (!WebPAnimDecoderGetInfo(dec, &anim_info)) { |   if (!WebPAnimDecoderGetInfo(dec, &anim_info)) { | ||||||
|     fprintf(stderr, "Error getting global info about the animation\n"); |     fprintf(stderr, "Error getting global info about the animation\n"); | ||||||
|     goto End; |     goto End; | ||||||
| @@ -181,25 +219,28 @@ static bool ReadAnimatedWebP(const char filename[], const std::string& file_str, | |||||||
|   image->bgcolor = anim_info.bgcolor; |   image->bgcolor = anim_info.bgcolor; | ||||||
| 
 | 
 | ||||||
|   // Allocate frames.
 |   // Allocate frames.
 | ||||||
|   AllocateFrames(image, anim_info.frame_count); |   if (!AllocateFrames(image, anim_info.frame_count)) return 0; | ||||||
| 
 | 
 | ||||||
|   // Decode frames.
 |   // Decode frames.
 | ||||||
|   while (WebPAnimDecoderHasMoreFrames(dec)) { |   while (WebPAnimDecoderHasMoreFrames(dec)) { | ||||||
|  |     DecodedFrame* curr_frame; | ||||||
|  |     uint8_t* curr_rgba; | ||||||
|     uint8_t* frame_rgba; |     uint8_t* frame_rgba; | ||||||
|     int timestamp; |     int timestamp; | ||||||
|  | 
 | ||||||
|     if (!WebPAnimDecoderGetNext(dec, &frame_rgba, ×tamp)) { |     if (!WebPAnimDecoderGetNext(dec, &frame_rgba, ×tamp)) { | ||||||
|       fprintf(stderr, "Error decoding frame #%u\n", frame_index); |       fprintf(stderr, "Error decoding frame #%u\n", frame_index); | ||||||
|       goto End; |       goto End; | ||||||
|     } |     } | ||||||
|     DecodedFrame* const curr_frame = &image->frames[frame_index]; |     curr_frame = &image->frames[frame_index]; | ||||||
|     uint8_t* const curr_rgba = curr_frame->rgba.data(); |     curr_rgba = curr_frame->rgba; | ||||||
|     curr_frame->duration = timestamp - prev_frame_timestamp; |     curr_frame->duration = timestamp - prev_frame_timestamp; | ||||||
|     curr_frame->is_key_frame = false;  // Unused.
 |     curr_frame->is_key_frame = 0;  // Unused.
 | ||||||
|     memcpy(curr_rgba, frame_rgba, |     memcpy(curr_rgba, frame_rgba, | ||||||
|            image->canvas_width * kNumChannels * image->canvas_height); |            image->canvas_width * kNumChannels * image->canvas_height); | ||||||
| 
 | 
 | ||||||
|     // Needed only because we may want to compare with GIF later.
 |     // Needed only because we may want to compare with GIF later.
 | ||||||
|     CleanupTransparentPixels(reinterpret_cast<uint32_t*>(curr_rgba), |     CleanupTransparentPixels((uint32_t*)curr_rgba, | ||||||
|                              image->canvas_width, image->canvas_height); |                              image->canvas_width, image->canvas_height); | ||||||
| 
 | 
 | ||||||
|     if (dump_frames && dump_ok) { |     if (dump_frames && dump_ok) { | ||||||
| @@ -224,12 +265,11 @@ static bool ReadAnimatedWebP(const char filename[], const std::string& file_str, | |||||||
| // GIF Decoding.
 | // GIF Decoding.
 | ||||||
| 
 | 
 | ||||||
| // Returns true if this is a valid GIF bitstream.
 | // Returns true if this is a valid GIF bitstream.
 | ||||||
| static bool IsGIF(const std::string& file_str) { | static int IsGIF(const WebPData* const data) { | ||||||
|   const char* const cstr = file_str.c_str(); |   return data->size > GIF_STAMP_LEN && | ||||||
|   return file_str.length() > GIF_STAMP_LEN && |          (!memcmp(GIF_STAMP, data->bytes, GIF_STAMP_LEN) || | ||||||
|          (!memcmp(GIF_STAMP, cstr, GIF_STAMP_LEN) || |           !memcmp(GIF87_STAMP, data->bytes, GIF_STAMP_LEN) || | ||||||
|           !memcmp(GIF87_STAMP, cstr, GIF_STAMP_LEN) || |           !memcmp(GIF89_STAMP, data->bytes, GIF_STAMP_LEN)); | ||||||
|           !memcmp(GIF89_STAMP, cstr, GIF_STAMP_LEN)); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef WEBP_HAVE_GIF | #ifdef WEBP_HAVE_GIF | ||||||
| @@ -247,13 +287,13 @@ static bool IsGIF(const std::string& file_str) { | |||||||
| #if !LOCAL_GIF_PREREQ(5, 0) | #if !LOCAL_GIF_PREREQ(5, 0) | ||||||
| 
 | 
 | ||||||
| // Added in v5.0
 | // Added in v5.0
 | ||||||
| typedef struct GraphicsControlBlock { | typedef struct { | ||||||
|   int DisposalMode; |   int DisposalMode; | ||||||
| #define DISPOSAL_UNSPECIFIED      0       // No disposal specified
 | #define DISPOSAL_UNSPECIFIED      0       // No disposal specified
 | ||||||
| #define DISPOSE_DO_NOT            1       // Leave image in place
 | #define DISPOSE_DO_NOT            1       // Leave image in place
 | ||||||
| #define DISPOSE_BACKGROUND        2       // Set area to background color
 | #define DISPOSE_BACKGROUND        2       // Set area to background color
 | ||||||
| #define DISPOSE_PREVIOUS          3       // Restore to previous content
 | #define DISPOSE_PREVIOUS          3       // Restore to previous content
 | ||||||
|   bool UserInputFlag;      // User confirmation required before disposal
 |   int UserInputFlag;       // User confirmation required before disposal
 | ||||||
|   int DelayTime;           // Pre-display delay in 0.01sec units
 |   int DelayTime;           // Pre-display delay in 0.01sec units
 | ||||||
|   int TransparentColor;    // Palette index for transparency, -1 if none
 |   int TransparentColor;    // Palette index for transparency, -1 if none
 | ||||||
| #define NO_TRANSPARENT_COLOR     -1 | #define NO_TRANSPARENT_COLOR     -1 | ||||||
| @@ -269,7 +309,7 @@ static int DGifExtensionToGCB(const size_t GifExtensionLength, | |||||||
|   gcb->UserInputFlag = (GifExtension[0] & 0x02) != 0; |   gcb->UserInputFlag = (GifExtension[0] & 0x02) != 0; | ||||||
|   gcb->DelayTime = GifExtension[1] | (GifExtension[2] << 8); |   gcb->DelayTime = GifExtension[1] | (GifExtension[2] << 8); | ||||||
|   if (GifExtension[0] & 0x01) { |   if (GifExtension[0] & 0x01) { | ||||||
|     gcb->TransparentColor = static_cast<int>(GifExtension[3]); |     gcb->TransparentColor = (int)GifExtension[3]; | ||||||
|   } else { |   } else { | ||||||
|     gcb->TransparentColor = NO_TRANSPARENT_COLOR; |     gcb->TransparentColor = NO_TRANSPARENT_COLOR; | ||||||
|   } |   } | ||||||
| @@ -283,7 +323,7 @@ static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex, | |||||||
|     return GIF_ERROR; |     return GIF_ERROR; | ||||||
|   } |   } | ||||||
|   gcb->DisposalMode = DISPOSAL_UNSPECIFIED; |   gcb->DisposalMode = DISPOSAL_UNSPECIFIED; | ||||||
|   gcb->UserInputFlag = false; |   gcb->UserInputFlag = 0; | ||||||
|   gcb->DelayTime = 0; |   gcb->DelayTime = 0; | ||||||
|   gcb->TransparentColor = NO_TRANSPARENT_COLOR; |   gcb->TransparentColor = NO_TRANSPARENT_COLOR; | ||||||
| 
 | 
 | ||||||
| @@ -291,7 +331,7 @@ static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex, | |||||||
|     ExtensionBlock* ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; |     ExtensionBlock* ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; | ||||||
|     if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { |     if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { | ||||||
|       return DGifExtensionToGCB( |       return DGifExtensionToGCB( | ||||||
|           ep->ByteCount, reinterpret_cast<const GifByteType*>(ep->Bytes), gcb); |           ep->ByteCount, (const GifByteType*)ep->Bytes, gcb); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   return GIF_ERROR; |   return GIF_ERROR; | ||||||
| @@ -313,11 +353,10 @@ static void GIFDisplayError(const GifFileType* const gif, int gif_error) { | |||||||
|   // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
 |   // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
 | ||||||
| #if LOCAL_GIF_PREREQ(4, 2) | #if LOCAL_GIF_PREREQ(4, 2) | ||||||
| #if LOCAL_GIF_PREREQ(5, 0) | #if LOCAL_GIF_PREREQ(5, 0) | ||||||
|   // Static string actually, hence the const char* cast.
 |   const char* error_str = | ||||||
|   const char* error_str = (const char*)GifErrorString( |       GifErrorString((gif == NULL) ? gif_error : gif->Error); | ||||||
|       (gif == NULL) ? gif_error : gif->Error); |  | ||||||
| #else | #else | ||||||
|   const char* error_str = (const char*)GifErrorString(); |   const char* error_str = GifErrorString(); | ||||||
|   (void)gif; |   (void)gif; | ||||||
| #endif | #endif | ||||||
|   if (error_str == NULL) error_str = "Unknown error"; |   if (error_str == NULL) error_str = "Unknown error"; | ||||||
| @@ -330,22 +369,23 @@ static void GIFDisplayError(const GifFileType* const gif, int gif_error) { | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool IsKeyFrameGIF(const GifImageDesc& prev_desc, int prev_dispose, | static int IsKeyFrameGIF(const GifImageDesc* prev_desc, int prev_dispose, | ||||||
|                          const DecodedFrame* const prev_frame, |                          const DecodedFrame* const prev_frame, | ||||||
|                          int canvas_width, int canvas_height) { |                          int canvas_width, int canvas_height) { | ||||||
|   if (prev_frame == NULL) return true; |   if (prev_frame == NULL) return 1; | ||||||
|   if (prev_dispose == DISPOSE_BACKGROUND) { |   if (prev_dispose == DISPOSE_BACKGROUND) { | ||||||
|     if (IsFullFrame(prev_desc.Width, prev_desc.Height, |     if (IsFullFrame(prev_desc->Width, prev_desc->Height, | ||||||
|                     canvas_width, canvas_height)) { |                     canvas_width, canvas_height)) { | ||||||
|       return true; |       return 1; | ||||||
|     } |     } | ||||||
|     if (prev_frame->is_key_frame) return true; |     if (prev_frame->is_key_frame) return 1; | ||||||
|   } |   } | ||||||
|   return false; |   return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int GetTransparentIndexGIF(GifFileType* gif) { | static int GetTransparentIndexGIF(GifFileType* gif) { | ||||||
|   GraphicsControlBlock first_gcb = GraphicsControlBlock(); |   GraphicsControlBlock first_gcb; | ||||||
|  |   memset(&first_gcb, 0, sizeof(first_gcb)); | ||||||
|   DGifSavedExtensionToGCB(gif, 0, &first_gcb); |   DGifSavedExtensionToGCB(gif, 0, &first_gcb); | ||||||
|   return first_gcb.TransparentColor; |   return first_gcb.TransparentColor; | ||||||
| } | } | ||||||
| @@ -370,13 +410,15 @@ static uint32_t GetBackgroundColorGIF(GifFileType* gif) { | |||||||
| 
 | 
 | ||||||
| // Find appropriate app extension and get loop count from the next extension.
 | // Find appropriate app extension and get loop count from the next extension.
 | ||||||
| static uint32_t GetLoopCountGIF(const GifFileType* const gif) { | static uint32_t GetLoopCountGIF(const GifFileType* const gif) { | ||||||
|   for (int i = 0; i < gif->ImageCount; ++i) { |   int i; | ||||||
|  |   for (i = 0; i < gif->ImageCount; ++i) { | ||||||
|     const SavedImage* const image = &gif->SavedImages[i]; |     const SavedImage* const image = &gif->SavedImages[i]; | ||||||
|     for (int j = 0; (j + 1) < image->ExtensionBlockCount; ++j) { |     int j; | ||||||
|  |     for (j = 0; (j + 1) < image->ExtensionBlockCount; ++j) { | ||||||
|       const ExtensionBlock* const eb1 = image->ExtensionBlocks + j; |       const ExtensionBlock* const eb1 = image->ExtensionBlocks + j; | ||||||
|       const ExtensionBlock* const eb2 = image->ExtensionBlocks + j + 1; |       const ExtensionBlock* const eb2 = image->ExtensionBlocks + j + 1; | ||||||
|       const char* const signature = reinterpret_cast<const char*>(eb1->Bytes); |       const char* const signature = (const char*)eb1->Bytes; | ||||||
|       const bool signature_is_ok = |       const int signature_is_ok = | ||||||
|           (eb1->Function == APPLICATION_EXT_FUNC_CODE) && |           (eb1->Function == APPLICATION_EXT_FUNC_CODE) && | ||||||
|           (eb1->ByteCount == 11) && |           (eb1->ByteCount == 11) && | ||||||
|           (!memcmp(signature, "NETSCAPE2.0", 11) || |           (!memcmp(signature, "NETSCAPE2.0", 11) || | ||||||
| @@ -384,8 +426,8 @@ static uint32_t GetLoopCountGIF(const GifFileType* const gif) { | |||||||
|       if (signature_is_ok && |       if (signature_is_ok && | ||||||
|           eb2->Function == CONTINUE_EXT_FUNC_CODE && eb2->ByteCount >= 3 && |           eb2->Function == CONTINUE_EXT_FUNC_CODE && eb2->ByteCount >= 3 && | ||||||
|           eb2->Bytes[0] == 1) { |           eb2->Bytes[0] == 1) { | ||||||
|         return (static_cast<uint32_t>(eb2->Bytes[2]) << 8) + |         return ((uint32_t)(eb2->Bytes[2]) << 8) + | ||||||
|                (static_cast<uint32_t>(eb2->Bytes[1]) << 0); |                ((uint32_t)(eb2->Bytes[1]) << 0); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -394,18 +436,19 @@ static uint32_t GetLoopCountGIF(const GifFileType* const gif) { | |||||||
| 
 | 
 | ||||||
| // Get duration of 'n'th frame in milliseconds.
 | // Get duration of 'n'th frame in milliseconds.
 | ||||||
| static int GetFrameDurationGIF(GifFileType* gif, int n) { | static int GetFrameDurationGIF(GifFileType* gif, int n) { | ||||||
|   GraphicsControlBlock gcb = GraphicsControlBlock(); |   GraphicsControlBlock gcb; | ||||||
|  |   memset(&gcb, 0, sizeof(gcb)); | ||||||
|   DGifSavedExtensionToGCB(gif, n, &gcb); |   DGifSavedExtensionToGCB(gif, n, &gcb); | ||||||
|   return gcb.DelayTime * 10; |   return gcb.DelayTime * 10; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Returns true if frame 'target' completely covers 'covered'.
 | // Returns true if frame 'target' completely covers 'covered'.
 | ||||||
| static bool CoversFrameGIF(const GifImageDesc& target, | static int CoversFrameGIF(const GifImageDesc* const target, | ||||||
|                            const GifImageDesc& covered) { |                           const GifImageDesc* const covered) { | ||||||
|   return target.Left <= covered.Left && |   return target->Left <= covered->Left && | ||||||
|          covered.Left + covered.Width <= target.Left + target.Width && |          covered->Left + covered->Width <= target->Left + target->Width && | ||||||
|          target.Top <= covered.Top && |          target->Top <= covered->Top && | ||||||
|          covered.Top + covered.Height <= target.Top + target.Height; |          covered->Top + covered->Height <= target->Top + target->Height; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void RemapPixelsGIF(const uint8_t* const src, | static void RemapPixelsGIF(const uint8_t* const src, | ||||||
| @@ -425,65 +468,72 @@ static void RemapPixelsGIF(const uint8_t* const src, | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool ReadFrameGIF(const SavedImage* const gif_image, | static int ReadFrameGIF(const SavedImage* const gif_image, | ||||||
|                         const ColorMapObject* cmap, int transparent_color, |                         const ColorMapObject* cmap, int transparent_color, | ||||||
|                         int out_stride, uint8_t* const dst) { |                         int out_stride, uint8_t* const dst) { | ||||||
|   const GifImageDesc& image_desc = gif_image->ImageDesc; |   const GifImageDesc* image_desc = &gif_image->ImageDesc; | ||||||
|   if (image_desc.ColorMap) { |   const uint8_t* in; | ||||||
|       cmap = image_desc.ColorMap; |   uint8_t* out; | ||||||
|   } |   int j; | ||||||
|  | 
 | ||||||
|  |   if (image_desc->ColorMap) cmap = image_desc->ColorMap; | ||||||
| 
 | 
 | ||||||
|   if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) { |   if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) { | ||||||
|     fprintf(stderr, "Potentially corrupt color map.\n"); |     fprintf(stderr, "Potentially corrupt color map.\n"); | ||||||
|       return false; |     return 0; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const uint8_t* in = reinterpret_cast<uint8_t*>(gif_image->RasterBits); |   in = (const uint8_t*)gif_image->RasterBits; | ||||||
|   uint8_t* out = |   out = dst + image_desc->Top * out_stride + image_desc->Left * kNumChannels; | ||||||
|       dst + image_desc.Top * out_stride + image_desc.Left * kNumChannels; |  | ||||||
| 
 | 
 | ||||||
|   for (int j = 0; j < image_desc.Height; ++j) { |   for (j = 0; j < image_desc->Height; ++j) { | ||||||
|       RemapPixelsGIF(in, cmap, transparent_color, image_desc.Width, out); |     RemapPixelsGIF(in, cmap, transparent_color, image_desc->Width, out); | ||||||
|       in += image_desc.Width; |     in += image_desc->Width; | ||||||
|     out += out_stride; |     out += out_stride; | ||||||
|   } |   } | ||||||
|   return true; |   return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Read animated GIF bitstream from 'filename' into 'AnimatedImage' struct.
 | // Read animated GIF bitstream from 'filename' into 'AnimatedImage' struct.
 | ||||||
| static bool ReadAnimatedGIF(const char filename[], AnimatedImage* const image, | static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image, | ||||||
|                             bool dump_frames, const char dump_folder[]) { |                            int dump_frames, const char dump_folder[]) { | ||||||
|   GifFileType* gif = DGifOpenFileName(filename, NULL); |   uint32_t frame_count; | ||||||
|  |   uint32_t canvas_width, canvas_height; | ||||||
|  |   uint32_t i; | ||||||
|  |   int gif_error; | ||||||
|  |   GifFileType* gif; | ||||||
|  | 
 | ||||||
|  |   gif = DGifOpenFileName(filename, NULL); | ||||||
|   if (gif == NULL) { |   if (gif == NULL) { | ||||||
|     fprintf(stderr, "Could not read file: %s.\n", filename); |     fprintf(stderr, "Could not read file: %s.\n", filename); | ||||||
|     return false; |     return 0; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const int gif_error = DGifSlurp(gif); |   gif_error = DGifSlurp(gif); | ||||||
|   if (gif_error != GIF_OK) { |   if (gif_error != GIF_OK) { | ||||||
|     fprintf(stderr, "Could not parse image: %s.\n", filename); |     fprintf(stderr, "Could not parse image: %s.\n", filename); | ||||||
|     GIFDisplayError(gif, gif_error); |     GIFDisplayError(gif, gif_error); | ||||||
|     DGifCloseFile(gif, NULL); |     DGifCloseFile(gif, NULL); | ||||||
|     return false; |     return 0; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Animation properties.
 |   // Animation properties.
 | ||||||
|   image->canvas_width = static_cast<uint32_t>(gif->SWidth); |   image->canvas_width = (uint32_t)gif->SWidth; | ||||||
|   image->canvas_height = static_cast<uint32_t>(gif->SHeight); |   image->canvas_height = (uint32_t)gif->SHeight; | ||||||
|   if (image->canvas_width > MAX_CANVAS_SIZE || |   if (image->canvas_width > MAX_CANVAS_SIZE || | ||||||
|       image->canvas_height > MAX_CANVAS_SIZE) { |       image->canvas_height > MAX_CANVAS_SIZE) { | ||||||
|     fprintf(stderr, "Invalid canvas dimension: %d x %d\n", |     fprintf(stderr, "Invalid canvas dimension: %d x %d\n", | ||||||
|             image->canvas_width, image->canvas_height); |             image->canvas_width, image->canvas_height); | ||||||
|     DGifCloseFile(gif, NULL); |     DGifCloseFile(gif, NULL); | ||||||
|     return false; |     return 0; | ||||||
|   } |   } | ||||||
|   image->loop_count = GetLoopCountGIF(gif); |   image->loop_count = GetLoopCountGIF(gif); | ||||||
|   image->bgcolor = GetBackgroundColorGIF(gif); |   image->bgcolor = GetBackgroundColorGIF(gif); | ||||||
| 
 | 
 | ||||||
|   const uint32_t frame_count = static_cast<uint32_t>(gif->ImageCount); |   frame_count = (uint32_t)gif->ImageCount; | ||||||
|   if (frame_count == 0) { |   if (frame_count == 0) { | ||||||
|     DGifCloseFile(gif, NULL); |     DGifCloseFile(gif, NULL); | ||||||
|     return false; |     return 0; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (image->canvas_width == 0 || image->canvas_height == 0) { |   if (image->canvas_width == 0 || image->canvas_height == 0) { | ||||||
| @@ -494,33 +544,38 @@ static bool ReadAnimatedGIF(const char filename[], AnimatedImage* const image, | |||||||
|     if (image->canvas_width == 0 || image->canvas_height == 0) { |     if (image->canvas_width == 0 || image->canvas_height == 0) { | ||||||
|       fprintf(stderr, "Invalid canvas size in GIF.\n"); |       fprintf(stderr, "Invalid canvas size in GIF.\n"); | ||||||
|       DGifCloseFile(gif, NULL); |       DGifCloseFile(gif, NULL); | ||||||
|       return false; |       return 0; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   // Allocate frames.
 |   // Allocate frames.
 | ||||||
|   AllocateFrames(image, frame_count); |   AllocateFrames(image, frame_count); | ||||||
| 
 | 
 | ||||||
|   const uint32_t canvas_width = image->canvas_width; |   canvas_width = image->canvas_width; | ||||||
|   const uint32_t canvas_height = image->canvas_height; |   canvas_height = image->canvas_height; | ||||||
| 
 | 
 | ||||||
|   // Decode and reconstruct frames.
 |   // Decode and reconstruct frames.
 | ||||||
|   for (uint32_t i = 0; i < frame_count; ++i) { |   for (i = 0; i < frame_count; ++i) { | ||||||
|     const int canvas_width_in_bytes = canvas_width * kNumChannels; |     const int canvas_width_in_bytes = canvas_width * kNumChannels; | ||||||
|     const SavedImage* const curr_gif_image = &gif->SavedImages[i]; |     const SavedImage* const curr_gif_image = &gif->SavedImages[i]; | ||||||
|     GraphicsControlBlock curr_gcb = GraphicsControlBlock(); |     GraphicsControlBlock curr_gcb; | ||||||
|  |     DecodedFrame* curr_frame; | ||||||
|  |     uint8_t* curr_rgba; | ||||||
|  | 
 | ||||||
|  |     memset(&curr_gcb, 0, sizeof(curr_gcb)); | ||||||
|     DGifSavedExtensionToGCB(gif, i, &curr_gcb); |     DGifSavedExtensionToGCB(gif, i, &curr_gcb); | ||||||
| 
 | 
 | ||||||
|     DecodedFrame* const curr_frame = &image->frames[i]; |     curr_frame = &image->frames[i]; | ||||||
|     uint8_t* const curr_rgba = curr_frame->rgba.data(); |     curr_rgba = curr_frame->rgba; | ||||||
|     curr_frame->duration = GetFrameDurationGIF(gif, i); |     curr_frame->duration = GetFrameDurationGIF(gif, i); | ||||||
| 
 | 
 | ||||||
|     if (i == 0) {  // Initialize as transparent.
 |     if (i == 0) {  // Initialize as transparent.
 | ||||||
|       curr_frame->is_key_frame = true; |       curr_frame->is_key_frame = 1; | ||||||
|       ZeroFillCanvas(curr_rgba, canvas_width, canvas_height); |       ZeroFillCanvas(curr_rgba, canvas_width, canvas_height); | ||||||
|     } else { |     } else { | ||||||
|       DecodedFrame* const prev_frame = &image->frames[i - 1]; |       DecodedFrame* const prev_frame = &image->frames[i - 1]; | ||||||
|       const GifImageDesc& prev_desc = gif->SavedImages[i - 1].ImageDesc; |       const GifImageDesc* const prev_desc = &gif->SavedImages[i - 1].ImageDesc; | ||||||
|       GraphicsControlBlock prev_gcb = GraphicsControlBlock(); |       GraphicsControlBlock prev_gcb; | ||||||
|  |       memset(&prev_gcb, 0, sizeof(prev_gcb)); | ||||||
|       DGifSavedExtensionToGCB(gif, i - 1, &prev_gcb); |       DGifSavedExtensionToGCB(gif, i - 1, &prev_gcb); | ||||||
| 
 | 
 | ||||||
|       curr_frame->is_key_frame = |       curr_frame->is_key_frame = | ||||||
| @@ -530,32 +585,35 @@ static bool ReadAnimatedGIF(const char filename[], AnimatedImage* const image, | |||||||
|       if (curr_frame->is_key_frame) {  // Initialize as transparent.
 |       if (curr_frame->is_key_frame) {  // Initialize as transparent.
 | ||||||
|         ZeroFillCanvas(curr_rgba, canvas_width, canvas_height); |         ZeroFillCanvas(curr_rgba, canvas_width, canvas_height); | ||||||
|       } else { |       } else { | ||||||
|  |         int prev_frame_disposed, curr_frame_opaque; | ||||||
|  |         int prev_frame_completely_covered; | ||||||
|         // Initialize with previous canvas.
 |         // Initialize with previous canvas.
 | ||||||
|         uint8_t* const prev_rgba = image->frames[i - 1].rgba.data(); |         uint8_t* const prev_rgba = image->frames[i - 1].rgba; | ||||||
|         CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height); |         CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height); | ||||||
| 
 | 
 | ||||||
|         // Dispose previous frame rectangle.
 |         // Dispose previous frame rectangle.
 | ||||||
|         bool prev_frame_disposed = |         prev_frame_disposed = | ||||||
|             (prev_gcb.DisposalMode == DISPOSE_BACKGROUND || |             (prev_gcb.DisposalMode == DISPOSE_BACKGROUND || | ||||||
|              prev_gcb.DisposalMode == DISPOSE_PREVIOUS); |              prev_gcb.DisposalMode == DISPOSE_PREVIOUS); | ||||||
|         bool curr_frame_opaque = |         curr_frame_opaque = | ||||||
|             (curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR); |             (curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR); | ||||||
|         bool prev_frame_completely_covered = |         prev_frame_completely_covered = | ||||||
|             curr_frame_opaque && |             curr_frame_opaque && | ||||||
|             CoversFrameGIF(curr_gif_image->ImageDesc, prev_desc); |             CoversFrameGIF(&curr_gif_image->ImageDesc, prev_desc); | ||||||
| 
 | 
 | ||||||
|         if (prev_frame_disposed && !prev_frame_completely_covered) { |         if (prev_frame_disposed && !prev_frame_completely_covered) { | ||||||
|           switch (prev_gcb.DisposalMode) { |           switch (prev_gcb.DisposalMode) { | ||||||
|             case DISPOSE_BACKGROUND: { |             case DISPOSE_BACKGROUND: { | ||||||
|               ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes, |               ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes, | ||||||
|                                 prev_desc.Left, prev_desc.Top, |                                 prev_desc->Left, prev_desc->Top, | ||||||
|                                 prev_desc.Width, prev_desc.Height); |                                 prev_desc->Width, prev_desc->Height); | ||||||
|               break; |               break; | ||||||
|             } |             } | ||||||
|             case DISPOSE_PREVIOUS: { |             case DISPOSE_PREVIOUS: { | ||||||
|               int src_frame_num = i - 2; |               int src_frame_num = i - 2; | ||||||
|               while (src_frame_num >= 0) { |               while (src_frame_num >= 0) { | ||||||
|                 GraphicsControlBlock src_frame_gcb = GraphicsControlBlock(); |                 GraphicsControlBlock src_frame_gcb; | ||||||
|  |                 memset(&src_frame_gcb, 0, sizeof(src_frame_gcb)); | ||||||
|                 DGifSavedExtensionToGCB(gif, src_frame_num, &src_frame_gcb); |                 DGifSavedExtensionToGCB(gif, src_frame_num, &src_frame_gcb); | ||||||
|                 if (src_frame_gcb.DisposalMode != DISPOSE_PREVIOUS) break; |                 if (src_frame_gcb.DisposalMode != DISPOSE_PREVIOUS) break; | ||||||
|                 --src_frame_num; |                 --src_frame_num; | ||||||
| @@ -564,17 +622,17 @@ static bool ReadAnimatedGIF(const char filename[], AnimatedImage* const image, | |||||||
|                 // Restore pixels inside previous frame rectangle to
 |                 // Restore pixels inside previous frame rectangle to
 | ||||||
|                 // corresponding pixels in source canvas.
 |                 // corresponding pixels in source canvas.
 | ||||||
|                 uint8_t* const src_frame_rgba = |                 uint8_t* const src_frame_rgba = | ||||||
|                     image->frames[src_frame_num].rgba.data(); |                     image->frames[src_frame_num].rgba; | ||||||
|                 CopyFrameRectangle(src_frame_rgba, curr_rgba, |                 CopyFrameRectangle(src_frame_rgba, curr_rgba, | ||||||
|                                    canvas_width_in_bytes, |                                    canvas_width_in_bytes, | ||||||
|                                    prev_desc.Left, prev_desc.Top, |                                    prev_desc->Left, prev_desc->Top, | ||||||
|                                    prev_desc.Width, prev_desc.Height); |                                    prev_desc->Width, prev_desc->Height); | ||||||
|               } else { |               } else { | ||||||
|                 // Source canvas doesn't exist. So clear previous frame
 |                 // Source canvas doesn't exist. So clear previous frame
 | ||||||
|                 // rectangle to background.
 |                 // rectangle to background.
 | ||||||
|                 ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes, |                 ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes, | ||||||
|                                   prev_desc.Left, prev_desc.Top, |                                   prev_desc->Left, prev_desc->Top, | ||||||
|                                   prev_desc.Width, prev_desc.Height); |                                   prev_desc->Width, prev_desc->Height); | ||||||
|               } |               } | ||||||
|               break; |               break; | ||||||
|             } |             } | ||||||
| @@ -589,67 +647,65 @@ static bool ReadAnimatedGIF(const char filename[], AnimatedImage* const image, | |||||||
|     if (!ReadFrameGIF(curr_gif_image, gif->SColorMap, curr_gcb.TransparentColor, |     if (!ReadFrameGIF(curr_gif_image, gif->SColorMap, curr_gcb.TransparentColor, | ||||||
|                       canvas_width_in_bytes, curr_rgba)) { |                       canvas_width_in_bytes, curr_rgba)) { | ||||||
|       DGifCloseFile(gif, NULL); |       DGifCloseFile(gif, NULL); | ||||||
|       return false; |       return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (dump_frames) { |     if (dump_frames) { | ||||||
|       if (!DumpFrame(filename, dump_folder, i, curr_rgba, |       if (!DumpFrame(filename, dump_folder, i, curr_rgba, | ||||||
|                      canvas_width, canvas_height)) { |                      canvas_width, canvas_height)) { | ||||||
|         DGifCloseFile(gif, NULL); |         DGifCloseFile(gif, NULL); | ||||||
|         return false; |         return 0; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   DGifCloseFile(gif, NULL); |   DGifCloseFile(gif, NULL); | ||||||
|   return true; |   return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #else | #else | ||||||
| 
 | 
 | ||||||
| static bool ReadAnimatedGIF(const char filename[], AnimatedImage* const image, | static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image, | ||||||
|                             bool dump_frames, const char dump_folder[]) { |                            int dump_frames, const char dump_folder[]) { | ||||||
|   (void)filename; |   (void)filename; | ||||||
|   (void)image; |   (void)image; | ||||||
|   (void)dump_frames; |   (void)dump_frames; | ||||||
|   (void)dump_folder; |   (void)dump_folder; | ||||||
|   fprintf(stderr, "GIF support not compiled. Please install the libgif-dev " |   fprintf(stderr, "GIF support not compiled. Please install the libgif-dev " | ||||||
|           "package before building.\n"); |           "package before building.\n"); | ||||||
|   return false; |   return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif  // WEBP_HAVE_GIF
 | #endif  // WEBP_HAVE_GIF
 | ||||||
| 
 | 
 | ||||||
| // -----------------------------------------------------------------------------
 | // -----------------------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
| static bool ReadFile(const char filename[], std::string* filestr) { | int ReadAnimatedImage(const char filename[], AnimatedImage* const image, | ||||||
|   ifstream fin(filename, ios::binary); |                       int dump_frames, const char dump_folder[]) { | ||||||
|   if (!fin.good()) return false; |   int ok = 0; | ||||||
|   ostringstream strout; |   WebPData webp_data; | ||||||
|   strout << fin.rdbuf(); |  | ||||||
|   *filestr = strout.str(); |  | ||||||
|   fin.close(); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| bool ReadAnimatedImage(const char filename[], AnimatedImage* const image, |   WebPDataInit(&webp_data); | ||||||
|                        bool dump_frames, const char dump_folder[]) { |   memset(image, 0, sizeof(*image)); | ||||||
|   std::string file_str; | 
 | ||||||
|   if (!ReadFile(filename, &file_str)) { |   if (!ExUtilReadFile(filename, &webp_data.bytes, &webp_data.size)) { | ||||||
|     fprintf(stderr, "Error reading file: %s\n", filename); |     fprintf(stderr, "Error reading file: %s\n", filename); | ||||||
|     return false; |     return 0; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (IsWebP(file_str)) { |   if (IsWebP(&webp_data)) { | ||||||
|     return ReadAnimatedWebP(filename, file_str, image, dump_frames, |     ok = ReadAnimatedWebP(filename, &webp_data, image, dump_frames, | ||||||
|                           dump_folder); |                           dump_folder); | ||||||
|   } else if (IsGIF(file_str)) { |   } else if (IsGIF(&webp_data)) { | ||||||
|     return ReadAnimatedGIF(filename, image, dump_frames, dump_folder); |     ok = ReadAnimatedGIF(filename, image, dump_frames, dump_folder); | ||||||
|   } else { |   } else { | ||||||
|     fprintf(stderr, |     fprintf(stderr, | ||||||
|             "Unknown file type: %s. Supported file types are WebP and GIF\n", |             "Unknown file type: %s. Supported file types are WebP and GIF\n", | ||||||
|             filename); |             filename); | ||||||
|     return false; |     ok = 0; | ||||||
|   } |   } | ||||||
|  |   if (!ok) ClearAnimatedImage(image); | ||||||
|  |   WebPDataClear(&webp_data); | ||||||
|  |   return ok; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void Accumulate(double v1, double v2, double* const max_diff, | static void Accumulate(double v1, double v2, double* const max_diff, | ||||||
| @@ -660,25 +716,27 @@ static void Accumulate(double v1, double v2, double* const max_diff, | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[], | void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[], | ||||||
|                     uint32_t width, uint32_t height, bool premultiply, |                     uint32_t width, uint32_t height, int premultiply, | ||||||
|                     int* const max_diff, double* const psnr) { |                     int* const max_diff, double* const psnr) { | ||||||
|   const uint32_t stride = width * kNumChannels; |   const uint32_t stride = width * kNumChannels; | ||||||
|   const int kAlphaChannel = kNumChannels - 1; |   const int kAlphaChannel = kNumChannels - 1; | ||||||
|   double f_max_diff = 0.; |   double f_max_diff = 0.; | ||||||
|   double sse = 0.; |   double sse = 0.; | ||||||
|   for (uint32_t y = 0; y < height; ++y) { |   uint32_t x, y; | ||||||
|     for (uint32_t x = 0; x < stride; x += kNumChannels) { |   for (y = 0; y < height; ++y) { | ||||||
|  |     for (x = 0; x < stride; x += kNumChannels) { | ||||||
|  |       int k; | ||||||
|       const size_t offset = y * stride + x; |       const size_t offset = y * stride + x; | ||||||
|       const int alpha1 = rgba1[offset + kAlphaChannel]; |       const int alpha1 = rgba1[offset + kAlphaChannel]; | ||||||
|       const int alpha2 = rgba2[offset + kAlphaChannel]; |       const int alpha2 = rgba2[offset + kAlphaChannel]; | ||||||
|       Accumulate(alpha1, alpha2, &f_max_diff, &sse); |       Accumulate(alpha1, alpha2, &f_max_diff, &sse); | ||||||
|       if (!premultiply) { |       if (!premultiply) { | ||||||
|         for (int k = 0; k < kAlphaChannel; ++k) { |         for (k = 0; k < kAlphaChannel; ++k) { | ||||||
|           Accumulate(rgba1[offset + k], rgba2[offset + k], &f_max_diff, &sse); |           Accumulate(rgba1[offset + k], rgba2[offset + k], &f_max_diff, &sse); | ||||||
|         } |         } | ||||||
|       } else { |       } else { | ||||||
|         // premultiply R/G/B channels with alpha value
 |         // premultiply R/G/B channels with alpha value
 | ||||||
|         for (int k = 0; k < kAlphaChannel; ++k) { |         for (k = 0; k < kAlphaChannel; ++k) { | ||||||
|           Accumulate(rgba1[offset + k] * alpha1 / 255., |           Accumulate(rgba1[offset + k] * alpha1 / 255., | ||||||
|                      rgba2[offset + k] * alpha2 / 255., |                      rgba2[offset + k] * alpha2 / 255., | ||||||
|                      &f_max_diff, &sse); |                      &f_max_diff, &sse); | ||||||
| @@ -686,11 +744,11 @@ void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[], | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   *max_diff = static_cast<int>(f_max_diff); |   *max_diff = (int)f_max_diff; | ||||||
|   if (*max_diff == 0) { |   if (*max_diff == 0) { | ||||||
|     *psnr = 99.;  // PSNR when images are identical.
 |     *psnr = 99.;  // PSNR when images are identical.
 | ||||||
|   } else { |   } else { | ||||||
|     sse /= stride * height; |     sse /= stride * height; | ||||||
|     *psnr = 10. * log10(255. * 255. / sse); |     *psnr = 4.3429448 * log(255. * 255. / sse); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -16,34 +16,48 @@ | |||||||
| #include "webp/config.h" | #include "webp/config.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #include <vector> |  | ||||||
|  |  | ||||||
| #include "webp/types.h" | #include "webp/types.h" | ||||||
|  |  | ||||||
| struct DecodedFrame { | #ifdef __cplusplus | ||||||
|   std::vector<uint8_t> rgba;  // Decoded and reconstructed full frame. | extern "C" { | ||||||
|   int duration;          // Frame duration in milliseconds. | #endif | ||||||
|   bool is_key_frame;     // True if this frame is a key-frame. |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct AnimatedImage { | typedef struct { | ||||||
|  |   uint8_t* rgba;         // Decoded and reconstructed full frame. | ||||||
|  |   int duration;          // Frame duration in milliseconds. | ||||||
|  |   int is_key_frame;      // True if this frame is a key-frame. | ||||||
|  | } DecodedFrame; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|   uint32_t canvas_width; |   uint32_t canvas_width; | ||||||
|   uint32_t canvas_height; |   uint32_t canvas_height; | ||||||
|   uint32_t bgcolor; |   uint32_t bgcolor; | ||||||
|   uint32_t loop_count; |   uint32_t loop_count; | ||||||
|   std::vector<DecodedFrame> frames; |   DecodedFrame* frames; | ||||||
| }; |   uint32_t num_frames; | ||||||
|  |   void* raw_mem; | ||||||
|  | } AnimatedImage; | ||||||
|  |  | ||||||
|  | // Deallocate everything in 'image' (but not the object itself). | ||||||
|  | void ClearAnimatedImage(AnimatedImage* const image); | ||||||
|  |  | ||||||
| // Read animated image file into 'AnimatedImage' struct. | // Read animated image file into 'AnimatedImage' struct. | ||||||
| // If 'dump_frames' is true, dump frames to 'dump_folder'. | // If 'dump_frames' is true, dump frames to 'dump_folder'. | ||||||
| bool ReadAnimatedImage(const char filename[], AnimatedImage* const image, | // Previous content of 'image' is obliterated. | ||||||
|                        bool dump_frames, const char dump_folder[]); | // Upon successful return, content of 'image' must be deleted by | ||||||
|  | // calling 'ClearAnimatedImage'. | ||||||
|  | int ReadAnimatedImage(const char filename[], AnimatedImage* const image, | ||||||
|  |                       int dump_frames, const char dump_folder[]); | ||||||
|  |  | ||||||
| // Given two RGBA buffers, calculate max pixel difference and PSNR. | // Given two RGBA buffers, calculate max pixel difference and PSNR. | ||||||
| // If 'premultiply' is true, R/G/B values will be pre-multiplied by the | // If 'premultiply' is true, R/G/B values will be pre-multiplied by the | ||||||
| // transparency before comparison. | // transparency before comparison. | ||||||
| void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[], | void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[], | ||||||
|                     uint32_t width, uint32_t height, bool premultiply, |                     uint32_t width, uint32_t height, int premultiply, | ||||||
|                     int* const max_diff, double* const psnr); |                     int* const max_diff, double* const psnr); | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | }    // extern "C" | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #endif  // WEBP_EXAMPLES_ANIM_UTIL_H_ | #endif  // WEBP_EXAMPLES_ANIM_UTIL_H_ | ||||||
|   | |||||||
| @@ -62,19 +62,13 @@ EXTRA_FLAGS += -DWEBP_USE_THREAD | |||||||
| EXTRA_LIBS += -lpthread | EXTRA_LIBS += -lpthread | ||||||
|  |  | ||||||
| # Extra flags to emulate C89 strictness with the full ANSI | # Extra flags to emulate C89 strictness with the full ANSI | ||||||
| EXTRA_C89_FLAGS = -Wextra -Wold-style-definition | EXTRA_FLAGS += -Wextra -Wold-style-definition | ||||||
| EXTRA_C89_FLAGS += -Wmissing-prototypes | EXTRA_FLAGS += -Wmissing-prototypes | ||||||
| EXTRA_C89_FLAGS += -Wmissing-declarations | EXTRA_FLAGS += -Wmissing-declarations | ||||||
| EXTRA_C89_FLAGS += -Wdeclaration-after-statement | EXTRA_FLAGS += -Wdeclaration-after-statement | ||||||
| EXTRA_C89_FLAGS += -Wshadow | EXTRA_FLAGS += -Wshadow | ||||||
| EXTRA_C89_FLAGS += -Wformat-security -Wformat-nonliteral | EXTRA_FLAGS += -Wformat-security -Wformat-nonliteral | ||||||
| # EXTRA_C89_FLAGS += -Wvla | # EXTRA_FLAGS += -Wvla | ||||||
|  |  | ||||||
| EXTRA_CXX_FLAGS = -Wextra |  | ||||||
| EXTRA_CXX_FLAGS += -Wmissing-declarations |  | ||||||
| EXTRA_CXX_FLAGS += -Wshadow |  | ||||||
| EXTRA_CXX_FLAGS += -Wformat-security -Wformat-nonliteral |  | ||||||
| # EXTRA_CXX_FLAGS += -Wvla |  | ||||||
|  |  | ||||||
| # SSE4.1-specific flags: | # SSE4.1-specific flags: | ||||||
| ifeq ($(HAVE_SSE41), 1) | ifeq ($(HAVE_SSE41), 1) | ||||||
| @@ -97,11 +91,8 @@ endif | |||||||
| AR = ar | AR = ar | ||||||
| ARFLAGS = r | ARFLAGS = r | ||||||
| CPPFLAGS = -Isrc/ -Wall | CPPFLAGS = -Isrc/ -Wall | ||||||
| COMMONFLAGS = -O3 -DNDEBUG $(EXTRA_FLAGS) | CFLAGS = -O3 -DNDEBUG $(EXTRA_FLAGS) | ||||||
| CC = gcc | CC = gcc | ||||||
| CFLAGS = $(COMMONFLAGS) $(EXTRA_C89_FLAGS) |  | ||||||
| CXX = g++ |  | ||||||
| CXXFLAGS = $(COMMONFLAGS) $(EXTRA_CXX_FLAGS) |  | ||||||
| INSTALL = install | INSTALL = install | ||||||
| GROFF = /usr/bin/groff | GROFF = /usr/bin/groff | ||||||
| COL = /usr/bin/col | COL = /usr/bin/col | ||||||
| @@ -299,18 +290,18 @@ HDRS = \ | |||||||
| OUT_LIBS = examples/libexample_util.a src/libwebpdecoder.a src/libwebp.a | OUT_LIBS = examples/libexample_util.a src/libwebpdecoder.a src/libwebp.a | ||||||
| EXTRA_LIB = src/libwebpextras.a | EXTRA_LIB = src/libwebpextras.a | ||||||
| OUT_EXAMPLES = examples/cwebp examples/dwebp | OUT_EXAMPLES = examples/cwebp examples/dwebp | ||||||
| EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux | EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux \ | ||||||
| EXTRA_CXX_EXAMPLES = examples/anim_diff |                  examples/anim_diff | ||||||
|  |  | ||||||
| OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES) | OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES) | ||||||
| ifeq ($(MAKECMDGOALS),clean) | ifeq ($(MAKECMDGOALS),clean) | ||||||
|   OUTPUT += $(EXTRA_EXAMPLES) $(EXTRA_CXX_EXAMPLES) |   OUTPUT += $(EXTRA_EXAMPLES) | ||||||
|   OUTPUT += src/demux/libwebpdemux.a src/mux/libwebpmux.a $(EXTRA_LIB) |   OUTPUT += src/demux/libwebpdemux.a src/mux/libwebpmux.a $(EXTRA_LIB) | ||||||
|   OUTPUT += examples/libgifdec.a examples/libanim_util.a |   OUTPUT += examples/libgifdec.a examples/libanim_util.a | ||||||
| endif | endif | ||||||
|  |  | ||||||
| ex: $(OUT_EXAMPLES) | ex: $(OUT_EXAMPLES) | ||||||
| all: ex $(EXTRA_EXAMPLES) $(EXTRA_CXX_EXAMPLES) | all: ex $(EXTRA_EXAMPLES) | ||||||
| extras: $(EXTRA_LIB) | extras: $(EXTRA_LIB) | ||||||
|  |  | ||||||
| $(EX_FORMAT_DEC_OBJS): %.o: %.h | $(EX_FORMAT_DEC_OBJS): %.o: %.h | ||||||
| @@ -326,9 +317,6 @@ src/utils/bit_writer.o: src/utils/endian_inl.h | |||||||
| %.o: %.c $(HDRS) | %.o: %.c $(HDRS) | ||||||
| 	$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ | 	$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ | ||||||
|  |  | ||||||
| %.o: %.cc $(HDRS) |  | ||||||
| 	$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ |  | ||||||
|  |  | ||||||
| examples/libanim_util.a: $(ANIM_UTIL_OBJS) | examples/libanim_util.a: $(ANIM_UTIL_OBJS) | ||||||
| examples/libexample_util.a: $(EX_UTIL_OBJS) | examples/libexample_util.a: $(EX_UTIL_OBJS) | ||||||
| examples/libgifdec.a: $(GIFDEC_OBJS) | examples/libgifdec.a: $(GIFDEC_OBJS) | ||||||
| @@ -349,7 +337,8 @@ examples/vwebp: examples/vwebp.o | |||||||
| examples/webpmux: examples/webpmux.o | examples/webpmux: examples/webpmux.o | ||||||
|  |  | ||||||
| examples/anim_diff: examples/libanim_util.a examples/libgifdec.a | examples/anim_diff: examples/libanim_util.a examples/libgifdec.a | ||||||
| examples/anim_diff: src/demux/libwebpdemux.a src/libwebp.a | examples/anim_diff: src/demux/libwebpdemux.a examples/libexample_util.a | ||||||
|  | examples/anim_diff: src/libwebp.a | ||||||
| examples/anim_diff: EXTRA_LIBS += $(GIF_LIBS) | examples/anim_diff: EXTRA_LIBS += $(GIF_LIBS) | ||||||
| examples/anim_diff: EXTRA_FLAGS += -DWEBP_HAVE_GIF | examples/anim_diff: EXTRA_FLAGS += -DWEBP_HAVE_GIF | ||||||
| examples/cwebp: examples/libexample_util.a src/libwebp.a | examples/cwebp: examples/libexample_util.a src/libwebp.a | ||||||
| @@ -370,9 +359,6 @@ examples/webpmux: src/libwebpdecoder.a | |||||||
| $(OUT_EXAMPLES) $(EXTRA_EXAMPLES): | $(OUT_EXAMPLES) $(EXTRA_EXAMPLES): | ||||||
| 	$(CC) -o $@ $^ $(LDFLAGS) | 	$(CC) -o $@ $^ $(LDFLAGS) | ||||||
|  |  | ||||||
| $(EXTRA_CXX_EXAMPLES): |  | ||||||
| 	$(CXX) -o $@ $^ $(LDFLAGS) |  | ||||||
|  |  | ||||||
| dist: DESTDIR := dist | dist: DESTDIR := dist | ||||||
| dist: OUT_EXAMPLES += $(EXTRA_EXAMPLES) | dist: OUT_EXAMPLES += $(EXTRA_EXAMPLES) | ||||||
| dist: all | dist: all | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user