mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 10:25:46 +01:00 
			
		
		
		
	libimageenc.a: extract image-saving code from dwebp
BUG=webp:277 Change-Id: I2c0e1df7b13b1f77474b5478048fef022e90f77a
This commit is contained in:
		
				
					committed by
					
						 James Zern
						James Zern
					
				
			
			
				
	
			
			
			
						parent
						
							9e478f808e
						
					
				
				
					commit
					af1ad3e2dd
				
			| @@ -128,6 +128,14 @@ if(WEBP_BUILD_CWEBP OR WEBP_BUILD_DWEBP) | ||||
|   add_library(imagedec ${imagedec_SRCS}) | ||||
|   target_link_libraries(imagedec webp ${WEBP_DEP_LIBRARIES} | ||||
|     ${WEBP_DEP_IMG_LIBRARIES}) | ||||
|  | ||||
|   # Image-encoding utility library. | ||||
|   set(imageenc_SRCS | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_enc.c | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_enc.h) | ||||
|   add_library(imageenc ${imageenc_SRCS}) | ||||
|   target_link_libraries(imageenc webp imageioutil | ||||
|     ${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES}) | ||||
| endif() | ||||
|  | ||||
| if(WEBP_BUILD_DWEBP) | ||||
| @@ -135,9 +143,9 @@ if(WEBP_BUILD_DWEBP) | ||||
|   include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS}) | ||||
|   add_executable(dwebp | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/examples/dwebp.c | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h | ||||
|   ) | ||||
|   target_link_libraries(dwebp imagedec webp exampleutil imageioutil | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h) | ||||
|   target_link_libraries(dwebp imagedec imageenc webp | ||||
|     exampleutil imageioutil | ||||
|     ${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES} | ||||
|   ) | ||||
| endif() | ||||
|   | ||||
							
								
								
									
										13
									
								
								Makefile.vc
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Makefile.vc
									
									
									
									
									
								
							| @@ -251,7 +251,7 @@ DSP_ENC_OBJS = \ | ||||
| EX_ANIM_UTIL_OBJS = \ | ||||
|     $(DIROBJ)\examples\anim_util.obj \ | ||||
|  | ||||
| EX_FORMAT_DEC_OBJS = \ | ||||
| IMAGEIO_DEC_OBJS = \ | ||||
|     $(DIROBJ)\imageio\image_dec.obj \ | ||||
|     $(DIROBJ)\imageio\jpegdec.obj \ | ||||
|     $(DIROBJ)\imageio\metadata.obj \ | ||||
| @@ -260,6 +260,9 @@ EX_FORMAT_DEC_OBJS = \ | ||||
|     $(DIROBJ)\imageio\webpdec.obj \ | ||||
|     $(DIROBJ)\imageio\wicdec.obj \ | ||||
|  | ||||
| IMAGEIO_ENC_OBJS = \ | ||||
|     $(DIROBJ)\imageio\image_enc.obj \ | ||||
|  | ||||
| EX_GIF_DEC_OBJS = \ | ||||
|     $(DIROBJ)\examples\gifdec.obj \ | ||||
|  | ||||
| @@ -339,9 +342,10 @@ anim_diff: $(DIRBIN)\anim_diff.exe | ||||
| $(DIRBIN)\anim_diff.exe: $(DIROBJ)\examples\anim_diff.obj $(EX_ANIM_UTIL_OBJS) | ||||
| $(DIRBIN)\anim_diff.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) | ||||
| $(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 $(IMAGEIO_DEC_OBJS) | ||||
| $(DIRBIN)\cwebp.exe: $(IMAGEIO_UTIL_OBJS) | ||||
| $(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj $(EX_FORMAT_DEC_OBJS) | ||||
| $(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj $(IMAGEIO_DEC_OBJS) | ||||
| $(DIRBIN)\dwebp.exe: $(IMAGEIO_ENC_OBJS) | ||||
| $(DIRBIN)\dwebp.exe: $(IMAGEIO_UTIL_OBJS) | ||||
| $(DIRBIN)\gif2webp.exe: $(DIROBJ)\examples\gif2webp.obj $(EX_GIF_DEC_OBJS) | ||||
| $(DIRBIN)\gif2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBPMUX) | ||||
| @@ -351,7 +355,8 @@ $(DIRBIN)\vwebp.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP) | ||||
| $(DIRBIN)\webpmux.exe: $(DIROBJ)\examples\webpmux.obj $(LIBWEBPMUX) | ||||
| $(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP) | ||||
| $(OUT_EXAMPLES): $(EX_UTIL_OBJS) $(LIBWEBP) | ||||
| $(EX_UTIL_OBJS) $(EX_FORMAT_DEC_OBJS) $(IMAGEIO_UTIL_OBJS): $(OUTPUT_DIRS) | ||||
| $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS): $(OUTPUT_DIRS) | ||||
| $(IMAGEIO_DEC_OBJS) $(IMAGEIO_ENC_OBJS): $(OUTPUT_DIRS) | ||||
| !ENDIF  # ARCH == ARM | ||||
|  | ||||
| experimental: | ||||
|   | ||||
							
								
								
									
										19
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								build.gradle
									
									
									
									
									
								
							| @@ -292,6 +292,24 @@ model { | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     imageenc(NativeLibrarySpec) { | ||||
|       binaries { | ||||
|         all { | ||||
|           lib library: "webp", linkage: "static" | ||||
|           lib library: "imageio_util", linkage: "static" | ||||
|         } | ||||
|       } | ||||
|       sources { | ||||
|         c { | ||||
|           source { | ||||
|             srcDir "./imageio" | ||||
|             include "image_enc.c" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     cwebp(NativeExecutableSpec) { | ||||
|       binaries { | ||||
|         all { | ||||
| @@ -316,6 +334,7 @@ model { | ||||
|         all { | ||||
|           lib library: "example_util", linkage: "static" | ||||
|           lib library: "imagedec", linkage: "static" | ||||
|           lib library: "imageenc", linkage: "static" | ||||
|           lib library: "imageio_util", linkage: "static" | ||||
|           lib library: "webp" | ||||
|         } | ||||
|   | ||||
| @@ -43,7 +43,7 @@ LOCAL_SRC_FILES := \ | ||||
|  | ||||
| LOCAL_CFLAGS := $(WEBP_CFLAGS) | ||||
| LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src | ||||
| LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webp | ||||
| LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec imageenc webp | ||||
|  | ||||
| LOCAL_MODULE := dwebp | ||||
|  | ||||
|   | ||||
| @@ -34,8 +34,11 @@ cwebp_LDADD += $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS) | ||||
| dwebp_SOURCES = dwebp.c stopwatch.h | ||||
| dwebp_CPPFLAGS  = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) | ||||
| dwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES) | ||||
| dwebp_LDADD  = libexample_util.la ../imageio/libimageio_util.la | ||||
| dwebp_LDADD += ../imageio/libimagedec.la ../src/libwebp.la | ||||
| dwebp_LDADD  = libexample_util.la | ||||
| dwebp_LDADD += ../imageio/libimagedec.la | ||||
| dwebp_LDADD += ../imageio/libimageenc.la | ||||
| dwebp_LDADD += ../imageio/libimageio_util.la | ||||
| dwebp_LDADD += ../src/libwebp.la | ||||
| dwebp_LDADD +=$(PNG_LIBS) $(JPEG_LIBS) | ||||
|  | ||||
| gif2webp_SOURCES = gif2webp.c gifdec.c gifdec.h | ||||
|   | ||||
							
								
								
									
										500
									
								
								examples/dwebp.c
									
									
									
									
									
								
							
							
						
						
									
										500
									
								
								examples/dwebp.c
									
									
									
									
									
								
							| @@ -20,28 +20,8 @@ | ||||
| #include "webp/config.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef WEBP_HAVE_PNG | ||||
| #include <png.h> | ||||
| #include <setjmp.h>   // note: this must be included *after* png.h | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_WINCODEC_H | ||||
| #ifdef __MINGW32__ | ||||
| #define INITGUID  // Without this GUIDs are declared extern and fail to link | ||||
| #endif | ||||
| #define CINTERFACE | ||||
| #define COBJMACROS | ||||
| #define _WIN32_IE 0x500  // Workaround bug in shlwapi.h when compiling C++ | ||||
|                          // code with COBJMACROS. | ||||
| #include <ole2.h>  // CreateStreamOnHGlobal() | ||||
| #include <shlwapi.h> | ||||
| #include <windows.h> | ||||
| #include <wincodec.h> | ||||
| #endif | ||||
|  | ||||
| #include "webp/decode.h" | ||||
| #include "../examples/example_util.h" | ||||
| #include "../imageio/imageio_util.h" | ||||
| #include "../imageio/image_enc.h" | ||||
| #include "../imageio/webpdec.h" | ||||
| #include "./stopwatch.h" | ||||
|  | ||||
| @@ -59,486 +39,18 @@ extern void* VP8GetCPUInfo;   // opaque forward declaration. | ||||
| #endif | ||||
| #endif  // WEBP_DLL | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| // Output types | ||||
| typedef enum { | ||||
|   PNG = 0, | ||||
|   PAM, | ||||
|   PPM, | ||||
|   PGM, | ||||
|   BMP, | ||||
|   TIFF, | ||||
|   RAW_YUV, | ||||
|   ALPHA_PLANE_ONLY,  // this is for experimenting only | ||||
|   // forced colorspace output (for testing, mostly) | ||||
|   RGB, RGBA, BGR, BGRA, ARGB, | ||||
|   RGBA_4444, RGB_565, | ||||
|   rgbA, bgrA, Argb, rgbA_4444, | ||||
|   YUV, YUVA | ||||
| } OutputFileFormat; | ||||
|  | ||||
| #ifdef HAVE_WINCODEC_H | ||||
|  | ||||
| #define IFS(fn)                                                     \ | ||||
|   do {                                                              \ | ||||
|     if (SUCCEEDED(hr)) {                                            \ | ||||
|       hr = (fn);                                                    \ | ||||
|       if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr);   \ | ||||
|     }                                                               \ | ||||
|   } while (0) | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| #define MAKE_REFGUID(x) (x) | ||||
| #else | ||||
| #define MAKE_REFGUID(x) &(x) | ||||
| #endif | ||||
|  | ||||
| static HRESULT CreateOutputStream(const char* out_file_name, | ||||
|                                   int write_to_mem, IStream** stream) { | ||||
|   HRESULT hr = S_OK; | ||||
|   if (write_to_mem) { | ||||
|     // Output to a memory buffer. This is freed when 'stream' is released. | ||||
|     IFS(CreateStreamOnHGlobal(NULL, TRUE, stream)); | ||||
|   } else { | ||||
|     IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream)); | ||||
|   } | ||||
|   if (FAILED(hr)) { | ||||
|     fprintf(stderr, "Error opening output file %s (%08lx)\n", | ||||
|             out_file_name, hr); | ||||
|   } | ||||
|   return hr; | ||||
| } | ||||
|  | ||||
| static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout, | ||||
|                              REFGUID container_guid, | ||||
|                              uint8_t* rgb, int stride, | ||||
|                              uint32_t width, uint32_t height, int has_alpha) { | ||||
|   HRESULT hr = S_OK; | ||||
|   IWICImagingFactory* factory = NULL; | ||||
|   IWICBitmapFrameEncode* frame = NULL; | ||||
|   IWICBitmapEncoder* encoder = NULL; | ||||
|   IStream* stream = NULL; | ||||
|   WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA | ||||
|                                               : GUID_WICPixelFormat24bppBGR; | ||||
|  | ||||
|   IFS(CoInitialize(NULL)); | ||||
|   IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL, | ||||
|                        CLSCTX_INPROC_SERVER, | ||||
|                        MAKE_REFGUID(IID_IWICImagingFactory), | ||||
|                        (LPVOID*)&factory)); | ||||
|   if (hr == REGDB_E_CLASSNOTREG) { | ||||
|     fprintf(stderr, | ||||
|             "Couldn't access Windows Imaging Component (are you running " | ||||
|             "Windows XP SP3 or newer?). PNG support not available. " | ||||
|             "Use -ppm or -pgm for available PPM and PGM formats.\n"); | ||||
|   } | ||||
|   IFS(CreateOutputStream(out_file_name, use_stdout, &stream)); | ||||
|   IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL, | ||||
|                                        &encoder)); | ||||
|   IFS(IWICBitmapEncoder_Initialize(encoder, stream, | ||||
|                                    WICBitmapEncoderNoCache)); | ||||
|   IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL)); | ||||
|   IFS(IWICBitmapFrameEncode_Initialize(frame, NULL)); | ||||
|   IFS(IWICBitmapFrameEncode_SetSize(frame, width, height)); | ||||
|   IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format)); | ||||
|   IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride, | ||||
|                                         height * stride, rgb)); | ||||
|   IFS(IWICBitmapFrameEncode_Commit(frame)); | ||||
|   IFS(IWICBitmapEncoder_Commit(encoder)); | ||||
|  | ||||
|   if (SUCCEEDED(hr) && use_stdout) { | ||||
|     HGLOBAL image; | ||||
|     IFS(GetHGlobalFromStream(stream, &image)); | ||||
|     if (SUCCEEDED(hr)) { | ||||
|       HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|       DWORD mode; | ||||
|       const BOOL update_mode = GetConsoleMode(std_output, &mode); | ||||
|       const void* const image_mem = GlobalLock(image); | ||||
|       DWORD bytes_written = 0; | ||||
|  | ||||
|       // Clear output processing if necessary, then output the image. | ||||
|       if (update_mode) SetConsoleMode(std_output, 0); | ||||
|       if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image), | ||||
|                      &bytes_written, NULL) || | ||||
|           bytes_written != GlobalSize(image)) { | ||||
|         hr = E_FAIL; | ||||
|       } | ||||
|       if (update_mode) SetConsoleMode(std_output, mode); | ||||
|       GlobalUnlock(image); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (frame != NULL) IUnknown_Release(frame); | ||||
|   if (encoder != NULL) IUnknown_Release(encoder); | ||||
|   if (factory != NULL) IUnknown_Release(factory); | ||||
|   if (stream != NULL) IUnknown_Release(stream); | ||||
|   return hr; | ||||
| } | ||||
|  | ||||
| static int WritePNG(const char* out_file_name, int use_stdout, | ||||
|                     const WebPDecBuffer* const buffer) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||
|  | ||||
|   return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout, | ||||
|                                  MAKE_REFGUID(GUID_ContainerFormatPng), | ||||
|                                  rgb, stride, width, height, has_alpha)); | ||||
| } | ||||
|  | ||||
| #elif defined(WEBP_HAVE_PNG)    // !HAVE_WINCODEC_H | ||||
| static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp dummy) { | ||||
|   (void)dummy;  // remove variable-unused warning | ||||
|   longjmp(png_jmpbuf(png), 1); | ||||
| } | ||||
|  | ||||
| static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||
|   volatile png_structp png; | ||||
|   volatile png_infop info; | ||||
|   png_uint_32 y; | ||||
|  | ||||
|   png = png_create_write_struct(PNG_LIBPNG_VER_STRING, | ||||
|                                 NULL, PNGErrorFunction, NULL); | ||||
|   if (png == NULL) { | ||||
|     return 0; | ||||
|   } | ||||
|   info = png_create_info_struct(png); | ||||
|   if (info == NULL) { | ||||
|     png_destroy_write_struct((png_structpp)&png, NULL); | ||||
|     return 0; | ||||
|   } | ||||
|   if (setjmp(png_jmpbuf(png))) { | ||||
|     png_destroy_write_struct((png_structpp)&png, (png_infopp)&info); | ||||
|     return 0; | ||||
|   } | ||||
|   png_init_io(png, out_file); | ||||
|   png_set_IHDR(png, info, width, height, 8, | ||||
|                has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB, | ||||
|                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, | ||||
|                PNG_FILTER_TYPE_DEFAULT); | ||||
|   png_write_info(png, info); | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     png_bytep row = rgb + y * stride; | ||||
|     png_write_rows(png, &row, 1); | ||||
|   } | ||||
|   png_write_end(png, info); | ||||
|   png_destroy_write_struct((png_structpp)&png, (png_infopp)&info); | ||||
|   return 1; | ||||
| } | ||||
| #else    // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG | ||||
| static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { | ||||
|   (void)out_file; | ||||
|   (void)buffer; | ||||
|   fprintf(stderr, "PNG support not compiled. Please install the libpng " | ||||
|           "development package before building.\n"); | ||||
|   fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n"); | ||||
|   return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const size_t bytes_per_px = alpha ? 4 : 3; | ||||
|   uint32_t y; | ||||
|  | ||||
|   if (alpha) { | ||||
|     fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n" | ||||
|                   "TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height); | ||||
|   } else { | ||||
|     fprintf(fout, "P6\n%u %u\n255\n", width, height); | ||||
|   } | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) { | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| // Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose. | ||||
| static int Write16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const rgba = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const uint32_t bytes_per_px = 2; | ||||
|   uint32_t y; | ||||
|  | ||||
|   fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height); | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(rgba + y * stride, width, bytes_per_px, fout) != bytes_per_px) { | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| static void PutLE16(uint8_t* const dst, uint32_t value) { | ||||
|   dst[0] = (value >> 0) & 0xff; | ||||
|   dst[1] = (value >> 8) & 0xff; | ||||
| } | ||||
|  | ||||
| static void PutLE32(uint8_t* const dst, uint32_t value) { | ||||
|   PutLE16(dst + 0, (value >>  0) & 0xffff); | ||||
|   PutLE16(dst + 2, (value >> 16) & 0xffff); | ||||
| } | ||||
|  | ||||
| #define BMP_HEADER_SIZE 54 | ||||
| static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const rgba = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const uint32_t bytes_per_px = has_alpha ? 4 : 3; | ||||
|   uint32_t y; | ||||
|   const uint32_t line_size = bytes_per_px * width; | ||||
|   const uint32_t bmp_stride = (line_size + 3) & ~3;   // pad to 4 | ||||
|   const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE; | ||||
|   uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 }; | ||||
|  | ||||
|   // bitmap file header | ||||
|   PutLE16(bmp_header + 0, 0x4d42);                // signature 'BM' | ||||
|   PutLE32(bmp_header + 2, total_size);            // size including header | ||||
|   PutLE32(bmp_header + 6, 0);                     // reserved | ||||
|   PutLE32(bmp_header + 10, BMP_HEADER_SIZE);      // offset to pixel array | ||||
|   // bitmap info header | ||||
|   PutLE32(bmp_header + 14, 40);                   // DIB header size | ||||
|   PutLE32(bmp_header + 18, width);                // dimensions | ||||
|   PutLE32(bmp_header + 22, -(int)height);         // vertical flip! | ||||
|   PutLE16(bmp_header + 26, 1);                    // number of planes | ||||
|   PutLE16(bmp_header + 28, bytes_per_px * 8);     // bits per pixel | ||||
|   PutLE32(bmp_header + 30, 0);                    // no compression (BI_RGB) | ||||
|   PutLE32(bmp_header + 34, 0);                    // image size (dummy) | ||||
|   PutLE32(bmp_header + 38, 2400);                 // x pixels/meter | ||||
|   PutLE32(bmp_header + 42, 2400);                 // y pixels/meter | ||||
|   PutLE32(bmp_header + 46, 0);                    // number of palette colors | ||||
|   PutLE32(bmp_header + 50, 0);                    // important color count | ||||
|  | ||||
|   // TODO(skal): color profile | ||||
|  | ||||
|   // write header | ||||
|   if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) { | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   // write pixel array | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(rgba + y * stride, line_size, 1, fout) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     // write padding zeroes | ||||
|     if (bmp_stride != line_size) { | ||||
|       const uint8_t zeroes[3] = { 0 }; | ||||
|       if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) { | ||||
|         return 0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
| #undef BMP_HEADER_SIZE | ||||
|  | ||||
| #define NUM_IFD_ENTRIES 15 | ||||
| #define EXTRA_DATA_SIZE 16 | ||||
| // 10b for signature/header + n * 12b entries + 4b for IFD terminator: | ||||
| #define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4) | ||||
| #define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE) | ||||
|  | ||||
| static int WriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const rgba = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const uint8_t bytes_per_px = has_alpha ? 4 : 3; | ||||
|   // For non-alpha case, we omit tag 0x152 (ExtraSamples). | ||||
|   const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES | ||||
|                                             : NUM_IFD_ENTRIES - 1; | ||||
|   uint8_t tiff_header[TIFF_HEADER_SIZE] = { | ||||
|     0x49, 0x49, 0x2a, 0x00,   // little endian signature | ||||
|     8, 0, 0, 0,               // offset to the unique IFD that follows | ||||
|     // IFD (offset = 8). Entries must be written in increasing tag order. | ||||
|     num_ifd_entries, 0,       // Number of entries in the IFD (12 bytes each). | ||||
|     0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    //  10: Width  (TBD) | ||||
|     0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    //  22: Height (TBD) | ||||
|     0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0,     //  34: BitsPerSample: 8888 | ||||
|         EXTRA_DATA_OFFSET + 0, 0, 0, 0, | ||||
|     0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    //  46: Compression: none | ||||
|     0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,    //  58: Photometric: RGB | ||||
|     0x11, 0x01, 4, 0, 1, 0, 0, 0,                //  70: Strips offset: | ||||
|         TIFF_HEADER_SIZE, 0, 0, 0,               //      data follows header | ||||
|     0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    //  82: Orientation: topleft | ||||
|     0x15, 0x01, 3, 0, 1, 0, 0, 0,                //  94: SamplesPerPixels | ||||
|         bytes_per_px, 0, 0, 0, | ||||
|     0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    // 106: Rows per strip (TBD) | ||||
|     0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0,    // 118: StripByteCount (TBD) | ||||
|     0x1a, 0x01, 5, 0, 1, 0, 0, 0,                // 130: X-resolution | ||||
|         EXTRA_DATA_OFFSET + 8, 0, 0, 0, | ||||
|     0x1b, 0x01, 5, 0, 1, 0, 0, 0,                // 142: Y-resolution | ||||
|         EXTRA_DATA_OFFSET + 8, 0, 0, 0, | ||||
|     0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    // 154: PlanarConfiguration | ||||
|     0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,    // 166: ResolutionUnit (inch) | ||||
|     0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    // 178: ExtraSamples: rgbA | ||||
|     0, 0, 0, 0,                                  // 190: IFD terminator | ||||
|     // EXTRA_DATA_OFFSET: | ||||
|     8, 0, 8, 0, 8, 0, 8, 0,      // BitsPerSample | ||||
|     72, 0, 0, 0, 1, 0, 0, 0      // 72 pixels/inch, for X/Y-resolution | ||||
|   }; | ||||
|   uint32_t y; | ||||
|  | ||||
|   // Fill placeholders in IFD: | ||||
|   PutLE32(tiff_header + 10 + 8, width); | ||||
|   PutLE32(tiff_header + 22 + 8, height); | ||||
|   PutLE32(tiff_header + 106 + 8, height); | ||||
|   PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height); | ||||
|   if (!has_alpha) PutLE32(tiff_header + 178, 0);  // IFD terminator | ||||
|  | ||||
|   // write header | ||||
|   if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) { | ||||
|     return 0; | ||||
|   } | ||||
|   // write pixel values | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(rgba + y * stride, bytes_per_px, width, fout) != width) { | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| #undef TIFF_HEADER_SIZE | ||||
| #undef EXTRA_DATA_OFFSET | ||||
| #undef EXTRA_DATA_SIZE | ||||
| #undef NUM_IFD_ENTRIES | ||||
|  | ||||
| static int WriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const a = buffer->u.YUVA.a; | ||||
|   const int a_stride = buffer->u.YUVA.a_stride; | ||||
|   uint32_t y; | ||||
|   assert(a != NULL); | ||||
|   fprintf(fout, "P5\n%u %u\n255\n", width, height); | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(a + y * a_stride, width, 1, fout) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| // format=PGM: save a grayscale PGM file using the IMC4 layout | ||||
| // (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for | ||||
| // viewing the samples, esp. for odd dimensions. | ||||
| // format=RAW_YUV: just save the Y/U/V/A planes sequentially without header. | ||||
| static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer, | ||||
|                          OutputFileFormat format) { | ||||
|   const int width = buffer->width; | ||||
|   const int height = buffer->height; | ||||
|   const WebPYUVABuffer* const yuv = &buffer->u.YUVA; | ||||
|   int ok = 1; | ||||
|   int y; | ||||
|   const int pad = (format == RAW_YUV) ? 0 : 1; | ||||
|   const int uv_width = (width + 1) / 2; | ||||
|   const int uv_height = (height + 1) / 2; | ||||
|   const int out_stride = (width + pad) & ~pad; | ||||
|   const int a_height = yuv->a ? height : 0; | ||||
|   if (format == PGM) { | ||||
|     fprintf(fout, "P5\n%d %d\n255\n", | ||||
|             out_stride, height + uv_height + a_height); | ||||
|   } | ||||
|   for (y = 0; ok && y < height; ++y) { | ||||
|     ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1); | ||||
|     if (format == PGM) { | ||||
|       if (width & 1) fputc(0, fout);    // padding byte | ||||
|     } | ||||
|   } | ||||
|   if (format == PGM) {   // IMC4 layout | ||||
|     for (y = 0; ok && y < uv_height; ++y) { | ||||
|       ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1); | ||||
|       ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1); | ||||
|     } | ||||
|   } else { | ||||
|     for (y = 0; ok && y < uv_height; ++y) { | ||||
|       ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1); | ||||
|     } | ||||
|     for (y = 0; ok && y < uv_height; ++y) { | ||||
|       ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1); | ||||
|     } | ||||
|   } | ||||
|   for (y = 0; ok && y < a_height; ++y) { | ||||
|     ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1); | ||||
|     if (format == PGM) { | ||||
|       if (width & 1) fputc(0, fout);    // padding byte | ||||
|     } | ||||
|   } | ||||
|   return ok; | ||||
| } | ||||
|  | ||||
| static int SaveOutput(const WebPDecBuffer* const buffer, | ||||
|                       OutputFileFormat format, const char* const out_file) { | ||||
|   FILE* fout = NULL; | ||||
|   int needs_open_file = 1; | ||||
|   const int use_stdout = !strcmp(out_file, "-"); | ||||
|                       WebPOutputFileFormat format, const char* const out_file) { | ||||
|   const int use_stdout = (out_file != NULL) && !strcmp(out_file, "-"); | ||||
|   int ok = 1; | ||||
|   Stopwatch stop_watch; | ||||
|  | ||||
|   if (verbose) { | ||||
|     StopwatchReset(&stop_watch); | ||||
|   } | ||||
|   ok = WebPSaveImage(buffer, format, out_file); | ||||
|  | ||||
| #ifdef HAVE_WINCODEC_H | ||||
|   needs_open_file = (format != PNG); | ||||
| #endif | ||||
|  | ||||
|   if (needs_open_file) { | ||||
|     fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(out_file, "wb"); | ||||
|     if (fout == NULL) { | ||||
|       fprintf(stderr, "Error opening output file %s\n", out_file); | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (format == PNG || | ||||
|       format == RGBA || format == BGRA || format == ARGB || | ||||
|       format == rgbA || format == bgrA || format == Argb) { | ||||
| #ifdef HAVE_WINCODEC_H | ||||
|     ok &= WritePNG(out_file, use_stdout, buffer); | ||||
| #else | ||||
|     ok &= WritePNG(fout, buffer); | ||||
| #endif | ||||
|   } else if (format == PAM) { | ||||
|     ok &= WritePPM(fout, buffer, 1); | ||||
|   } else if (format == PPM || format == RGB || format == BGR) { | ||||
|     ok &= WritePPM(fout, buffer, 0); | ||||
|   } else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) { | ||||
|     ok &= Write16bAsPGM(fout, buffer); | ||||
|   } else if (format == BMP) { | ||||
|     ok &= WriteBMP(fout, buffer); | ||||
|   } else if (format == TIFF) { | ||||
|     ok &= WriteTIFF(fout, buffer); | ||||
|   } else if (format == PGM || format == RAW_YUV || | ||||
|              format == YUV || format == YUVA) { | ||||
|     ok &= WritePGMOrYUV(fout, buffer, format == RAW_YUV ? RAW_YUV : PGM); | ||||
|   } else if (format == ALPHA_PLANE_ONLY) { | ||||
|     ok &= WriteAlphaPlane(fout, buffer); | ||||
|   } | ||||
|   if (fout != NULL && fout != stdout) { | ||||
|     fclose(fout); | ||||
|   } | ||||
|   if (ok) { | ||||
|     if (!quiet) { | ||||
|       if (use_stdout) { | ||||
| @@ -600,7 +112,7 @@ static const char* const kFormatType[] = { | ||||
| }; | ||||
|  | ||||
| static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config, | ||||
|                                        OutputFileFormat format, | ||||
|                                        WebPOutputFileFormat format, | ||||
|                                        int use_external_memory) { | ||||
|   uint8_t* external_buffer = NULL; | ||||
|   WebPDecBuffer* const output_buffer = &config->output; | ||||
| @@ -671,7 +183,7 @@ int main(int argc, const char *argv[]) { | ||||
|   WebPDecoderConfig config; | ||||
|   WebPDecBuffer* const output_buffer = &config.output; | ||||
|   WebPBitstreamFeatures* const bitstream = &config.input; | ||||
|   OutputFileFormat format = PNG; | ||||
|   WebPOutputFileFormat format = PNG; | ||||
|   uint8_t* external_buffer = NULL; | ||||
|   int use_external_memory = 0; | ||||
|   const uint8_t* data = NULL; | ||||
|   | ||||
| @@ -34,3 +34,19 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src | ||||
| LOCAL_MODULE := imagedec | ||||
|  | ||||
| include $(BUILD_STATIC_LIBRARY) | ||||
|  | ||||
| ################################################################################ | ||||
| # libimageenc | ||||
|  | ||||
| include $(CLEAR_VARS) | ||||
|  | ||||
| LOCAL_SRC_FILES := \ | ||||
|     image_enc.c \ | ||||
|  | ||||
| LOCAL_CFLAGS := $(WEBP_CFLAGS) | ||||
| LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src | ||||
| LOCAL_STATIC_LIBRARIES := imageio_util | ||||
|  | ||||
| LOCAL_MODULE := imageenc | ||||
|  | ||||
| include $(BUILD_STATIC_LIBRARY) | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src | ||||
| noinst_LTLIBRARIES = libimageio_util.la libimagedec.la | ||||
| noinst_LTLIBRARIES = libimageio_util.la libimagedec.la libimageenc.la | ||||
|  | ||||
| noinst_HEADERS = | ||||
| noinst_HEADERS += ../src/webp/decode.h | ||||
| @@ -17,3 +17,7 @@ libimagedec_la_SOURCES += webpdec.c webpdec.h | ||||
| libimagedec_la_SOURCES += wicdec.c wicdec.h | ||||
| libimagedec_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES) | ||||
| libimagedec_la_CPPFLAGS += $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) | ||||
|  | ||||
| libimageenc_la_SOURCES  = image_enc.c image_enc.h | ||||
| libimageenc_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES) | ||||
| libimageenc_la_CPPFLAGS += $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) | ||||
|   | ||||
							
								
								
									
										558
									
								
								imageio/image_enc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										558
									
								
								imageio/image_enc.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,558 @@ | ||||
| // Copyright 2016 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Use of this source code is governed by a BSD-style license | ||||
| // that can be found in the COPYING file in the root of the source | ||||
| // tree. An additional intellectual property rights grant can be found | ||||
| // in the file PATENTS. All contributing project authors may | ||||
| // be found in the AUTHORS file in the root of the source tree. | ||||
| // ----------------------------------------------------------------------------- | ||||
| // | ||||
| // Save image | ||||
|  | ||||
| #include "./image_enc.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #ifdef WEBP_HAVE_PNG | ||||
| #include <png.h> | ||||
| #include <setjmp.h>   // note: this must be included *after* png.h | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_WINCODEC_H | ||||
| #ifdef __MINGW32__ | ||||
| #define INITGUID  // Without this GUIDs are declared extern and fail to link | ||||
| #endif | ||||
| #define CINTERFACE | ||||
| #define COBJMACROS | ||||
| #define _WIN32_IE 0x500  // Workaround bug in shlwapi.h when compiling C++ | ||||
|                          // code with COBJMACROS. | ||||
| #include <ole2.h>  // CreateStreamOnHGlobal() | ||||
| #include <shlwapi.h> | ||||
| #include <windows.h> | ||||
| #include <wincodec.h> | ||||
| #endif | ||||
|  | ||||
| #include "./imageio_util.h" | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // PNG | ||||
|  | ||||
| #ifdef HAVE_WINCODEC_H | ||||
|  | ||||
| #define IFS(fn)                                                     \ | ||||
|   do {                                                              \ | ||||
|     if (SUCCEEDED(hr)) {                                            \ | ||||
|       hr = (fn);                                                    \ | ||||
|       if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr);   \ | ||||
|     }                                                               \ | ||||
|   } while (0) | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| #define MAKE_REFGUID(x) (x) | ||||
| #else | ||||
| #define MAKE_REFGUID(x) &(x) | ||||
| #endif | ||||
|  | ||||
| static HRESULT CreateOutputStream(const char* out_file_name, | ||||
|                                   int write_to_mem, IStream** stream) { | ||||
|   HRESULT hr = S_OK; | ||||
|   if (write_to_mem) { | ||||
|     // Output to a memory buffer. This is freed when 'stream' is released. | ||||
|     IFS(CreateStreamOnHGlobal(NULL, TRUE, stream)); | ||||
|   } else { | ||||
|     IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream)); | ||||
|   } | ||||
|   if (FAILED(hr)) { | ||||
|     fprintf(stderr, "Error opening output file %s (%08lx)\n", | ||||
|             out_file_name, hr); | ||||
|   } | ||||
|   return hr; | ||||
| } | ||||
|  | ||||
| static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout, | ||||
|                              REFGUID container_guid, | ||||
|                              uint8_t* rgb, int stride, | ||||
|                              uint32_t width, uint32_t height, int has_alpha) { | ||||
|   HRESULT hr = S_OK; | ||||
|   IWICImagingFactory* factory = NULL; | ||||
|   IWICBitmapFrameEncode* frame = NULL; | ||||
|   IWICBitmapEncoder* encoder = NULL; | ||||
|   IStream* stream = NULL; | ||||
|   WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA | ||||
|                                               : GUID_WICPixelFormat24bppBGR; | ||||
|  | ||||
|   if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG; | ||||
|  | ||||
|   IFS(CoInitialize(NULL)); | ||||
|   IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL, | ||||
|                        CLSCTX_INPROC_SERVER, | ||||
|                        MAKE_REFGUID(IID_IWICImagingFactory), | ||||
|                        (LPVOID*)&factory)); | ||||
|   if (hr == REGDB_E_CLASSNOTREG) { | ||||
|     fprintf(stderr, | ||||
|             "Couldn't access Windows Imaging Component (are you running " | ||||
|             "Windows XP SP3 or newer?). PNG support not available. " | ||||
|             "Use -ppm or -pgm for available PPM and PGM formats.\n"); | ||||
|   } | ||||
|   IFS(CreateOutputStream(out_file_name, use_stdout, &stream)); | ||||
|   IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL, | ||||
|                                        &encoder)); | ||||
|   IFS(IWICBitmapEncoder_Initialize(encoder, stream, | ||||
|                                    WICBitmapEncoderNoCache)); | ||||
|   IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL)); | ||||
|   IFS(IWICBitmapFrameEncode_Initialize(frame, NULL)); | ||||
|   IFS(IWICBitmapFrameEncode_SetSize(frame, width, height)); | ||||
|   IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format)); | ||||
|   IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride, | ||||
|                                         height * stride, rgb)); | ||||
|   IFS(IWICBitmapFrameEncode_Commit(frame)); | ||||
|   IFS(IWICBitmapEncoder_Commit(encoder)); | ||||
|  | ||||
|   if (SUCCEEDED(hr) && use_stdout) { | ||||
|     HGLOBAL image; | ||||
|     IFS(GetHGlobalFromStream(stream, &image)); | ||||
|     if (SUCCEEDED(hr)) { | ||||
|       HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|       DWORD mode; | ||||
|       const BOOL update_mode = GetConsoleMode(std_output, &mode); | ||||
|       const void* const image_mem = GlobalLock(image); | ||||
|       DWORD bytes_written = 0; | ||||
|  | ||||
|       // Clear output processing if necessary, then output the image. | ||||
|       if (update_mode) SetConsoleMode(std_output, 0); | ||||
|       if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image), | ||||
|                      &bytes_written, NULL) || | ||||
|           bytes_written != GlobalSize(image)) { | ||||
|         hr = E_FAIL; | ||||
|       } | ||||
|       if (update_mode) SetConsoleMode(std_output, mode); | ||||
|       GlobalUnlock(image); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (frame != NULL) IUnknown_Release(frame); | ||||
|   if (encoder != NULL) IUnknown_Release(encoder); | ||||
|   if (factory != NULL) IUnknown_Release(factory); | ||||
|   if (stream != NULL) IUnknown_Release(stream); | ||||
|   return hr; | ||||
| } | ||||
|  | ||||
| int WebPWritePNG(const char* out_file_name, int use_stdout, | ||||
|                  const WebPDecBuffer* const buffer) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||
|  | ||||
|   return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout, | ||||
|                                  MAKE_REFGUID(GUID_ContainerFormatPng), | ||||
|                                  rgb, stride, width, height, has_alpha)); | ||||
| } | ||||
|  | ||||
| #elif defined(WEBP_HAVE_PNG)    // !HAVE_WINCODEC_H | ||||
| static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp dummy) { | ||||
|   (void)dummy;  // remove variable-unused warning | ||||
|   longjmp(png_jmpbuf(png), 1); | ||||
| } | ||||
|  | ||||
| int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||
|   volatile png_structp png; | ||||
|   volatile png_infop info; | ||||
|   png_uint_32 y; | ||||
|  | ||||
|   if (out_file == NULL || buffer == NULL) return 0; | ||||
|  | ||||
|   png = png_create_write_struct(PNG_LIBPNG_VER_STRING, | ||||
|                                 NULL, PNGErrorFunction, NULL); | ||||
|   if (png == NULL) { | ||||
|     return 0; | ||||
|   } | ||||
|   info = png_create_info_struct(png); | ||||
|   if (info == NULL) { | ||||
|     png_destroy_write_struct((png_structpp)&png, NULL); | ||||
|     return 0; | ||||
|   } | ||||
|   if (setjmp(png_jmpbuf(png))) { | ||||
|     png_destroy_write_struct((png_structpp)&png, (png_infopp)&info); | ||||
|     return 0; | ||||
|   } | ||||
|   png_init_io(png, out_file); | ||||
|   png_set_IHDR(png, info, width, height, 8, | ||||
|                has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB, | ||||
|                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, | ||||
|                PNG_FILTER_TYPE_DEFAULT); | ||||
|   png_write_info(png, info); | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     png_bytep row = rgb + y * stride; | ||||
|     png_write_rows(png, &row, 1); | ||||
|   } | ||||
|   png_write_end(png, info); | ||||
|   png_destroy_write_struct((png_structpp)&png, (png_infopp)&info); | ||||
|   return 1; | ||||
| } | ||||
| #else    // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG | ||||
| int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   if (fout == NULL || buffer == NULL) return 0; | ||||
|  | ||||
|   fprintf(stderr, "PNG support not compiled. Please install the libpng " | ||||
|           "development package before building.\n"); | ||||
|   fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n"); | ||||
|   return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // PPM / PAM | ||||
|  | ||||
| static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer, | ||||
|                        int alpha) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const rgb = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const size_t bytes_per_px = alpha ? 4 : 3; | ||||
|   uint32_t y; | ||||
|  | ||||
|   if (fout == NULL || buffer == NULL || rgb == NULL) return 0; | ||||
|  | ||||
|   if (alpha) { | ||||
|     fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n" | ||||
|                   "TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height); | ||||
|   } else { | ||||
|     fprintf(fout, "P6\n%u %u\n255\n", width, height); | ||||
|   } | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) { | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| int WebPWritePPM(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   return WritePPMPAM(fout, buffer, 0); | ||||
| } | ||||
|  | ||||
| int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   return WritePPMPAM(fout, buffer, 1); | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Raw PGM | ||||
|  | ||||
| // Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose. | ||||
| int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const rgba = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const uint32_t bytes_per_px = 2; | ||||
|   uint32_t y; | ||||
|  | ||||
|   if (fout == NULL || buffer == NULL || rgba == NULL) return 0; | ||||
|  | ||||
|   fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height); | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(rgba + y * stride, width, bytes_per_px, fout) != bytes_per_px) { | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // BMP | ||||
|  | ||||
| static void PutLE16(uint8_t* const dst, uint32_t value) { | ||||
|   dst[0] = (value >> 0) & 0xff; | ||||
|   dst[1] = (value >> 8) & 0xff; | ||||
| } | ||||
|  | ||||
| static void PutLE32(uint8_t* const dst, uint32_t value) { | ||||
|   PutLE16(dst + 0, (value >>  0) & 0xffff); | ||||
|   PutLE16(dst + 2, (value >> 16) & 0xffff); | ||||
| } | ||||
|  | ||||
| #define BMP_HEADER_SIZE 54 | ||||
| int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const rgba = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const uint32_t bytes_per_px = has_alpha ? 4 : 3; | ||||
|   uint32_t y; | ||||
|   const uint32_t line_size = bytes_per_px * width; | ||||
|   const uint32_t bmp_stride = (line_size + 3) & ~3;   // pad to 4 | ||||
|   const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE; | ||||
|   uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 }; | ||||
|  | ||||
|   if (fout == NULL || buffer == NULL || rgba == NULL) return 0; | ||||
|  | ||||
|   // bitmap file header | ||||
|   PutLE16(bmp_header + 0, 0x4d42);                // signature 'BM' | ||||
|   PutLE32(bmp_header + 2, total_size);            // size including header | ||||
|   PutLE32(bmp_header + 6, 0);                     // reserved | ||||
|   PutLE32(bmp_header + 10, BMP_HEADER_SIZE);      // offset to pixel array | ||||
|   // bitmap info header | ||||
|   PutLE32(bmp_header + 14, 40);                   // DIB header size | ||||
|   PutLE32(bmp_header + 18, width);                // dimensions | ||||
|   PutLE32(bmp_header + 22, -(int)height);         // vertical flip! | ||||
|   PutLE16(bmp_header + 26, 1);                    // number of planes | ||||
|   PutLE16(bmp_header + 28, bytes_per_px * 8);     // bits per pixel | ||||
|   PutLE32(bmp_header + 30, 0);                    // no compression (BI_RGB) | ||||
|   PutLE32(bmp_header + 34, 0);                    // image size (dummy) | ||||
|   PutLE32(bmp_header + 38, 2400);                 // x pixels/meter | ||||
|   PutLE32(bmp_header + 42, 2400);                 // y pixels/meter | ||||
|   PutLE32(bmp_header + 46, 0);                    // number of palette colors | ||||
|   PutLE32(bmp_header + 50, 0);                    // important color count | ||||
|  | ||||
|   // TODO(skal): color profile | ||||
|  | ||||
|   // write header | ||||
|   if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) { | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   // write pixel array | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(rgba + y * stride, line_size, 1, fout) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     // write padding zeroes | ||||
|     if (bmp_stride != line_size) { | ||||
|       const uint8_t zeroes[3] = { 0 }; | ||||
|       if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) { | ||||
|         return 0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
| #undef BMP_HEADER_SIZE | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // TIFF | ||||
|  | ||||
| #define NUM_IFD_ENTRIES 15 | ||||
| #define EXTRA_DATA_SIZE 16 | ||||
| // 10b for signature/header + n * 12b entries + 4b for IFD terminator: | ||||
| #define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4) | ||||
| #define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE) | ||||
|  | ||||
| int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const int has_alpha = WebPIsAlphaMode(buffer->colorspace); | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const rgba = buffer->u.RGBA.rgba; | ||||
|   const int stride = buffer->u.RGBA.stride; | ||||
|   const uint8_t bytes_per_px = has_alpha ? 4 : 3; | ||||
|   // For non-alpha case, we omit tag 0x152 (ExtraSamples). | ||||
|   const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES | ||||
|                                             : NUM_IFD_ENTRIES - 1; | ||||
|   uint8_t tiff_header[TIFF_HEADER_SIZE] = { | ||||
|     0x49, 0x49, 0x2a, 0x00,   // little endian signature | ||||
|     8, 0, 0, 0,               // offset to the unique IFD that follows | ||||
|     // IFD (offset = 8). Entries must be written in increasing tag order. | ||||
|     num_ifd_entries, 0,       // Number of entries in the IFD (12 bytes each). | ||||
|     0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    //  10: Width  (TBD) | ||||
|     0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    //  22: Height (TBD) | ||||
|     0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0,     //  34: BitsPerSample: 8888 | ||||
|         EXTRA_DATA_OFFSET + 0, 0, 0, 0, | ||||
|     0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    //  46: Compression: none | ||||
|     0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,    //  58: Photometric: RGB | ||||
|     0x11, 0x01, 4, 0, 1, 0, 0, 0,                //  70: Strips offset: | ||||
|         TIFF_HEADER_SIZE, 0, 0, 0,               //      data follows header | ||||
|     0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    //  82: Orientation: topleft | ||||
|     0x15, 0x01, 3, 0, 1, 0, 0, 0,                //  94: SamplesPerPixels | ||||
|         bytes_per_px, 0, 0, 0, | ||||
|     0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    // 106: Rows per strip (TBD) | ||||
|     0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0,    // 118: StripByteCount (TBD) | ||||
|     0x1a, 0x01, 5, 0, 1, 0, 0, 0,                // 130: X-resolution | ||||
|         EXTRA_DATA_OFFSET + 8, 0, 0, 0, | ||||
|     0x1b, 0x01, 5, 0, 1, 0, 0, 0,                // 142: Y-resolution | ||||
|         EXTRA_DATA_OFFSET + 8, 0, 0, 0, | ||||
|     0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    // 154: PlanarConfiguration | ||||
|     0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,    // 166: ResolutionUnit (inch) | ||||
|     0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    // 178: ExtraSamples: rgbA | ||||
|     0, 0, 0, 0,                                  // 190: IFD terminator | ||||
|     // EXTRA_DATA_OFFSET: | ||||
|     8, 0, 8, 0, 8, 0, 8, 0,      // BitsPerSample | ||||
|     72, 0, 0, 0, 1, 0, 0, 0      // 72 pixels/inch, for X/Y-resolution | ||||
|   }; | ||||
|   uint32_t y; | ||||
|  | ||||
|   if (fout == NULL || buffer == NULL || rgba == NULL) return 0; | ||||
|  | ||||
|   // Fill placeholders in IFD: | ||||
|   PutLE32(tiff_header + 10 + 8, width); | ||||
|   PutLE32(tiff_header + 22 + 8, height); | ||||
|   PutLE32(tiff_header + 106 + 8, height); | ||||
|   PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height); | ||||
|   if (!has_alpha) PutLE32(tiff_header + 178, 0);  // IFD terminator | ||||
|  | ||||
|   // write header | ||||
|   if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) { | ||||
|     return 0; | ||||
|   } | ||||
|   // write pixel values | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(rgba + y * stride, bytes_per_px, width, fout) != width) { | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| #undef TIFF_HEADER_SIZE | ||||
| #undef EXTRA_DATA_OFFSET | ||||
| #undef EXTRA_DATA_SIZE | ||||
| #undef NUM_IFD_ENTRIES | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Raw Alpha | ||||
|  | ||||
| int WebPWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const uint32_t width = buffer->width; | ||||
|   const uint32_t height = buffer->height; | ||||
|   const uint8_t* const a = buffer->u.YUVA.a; | ||||
|   const int a_stride = buffer->u.YUVA.a_stride; | ||||
|   uint32_t y; | ||||
|  | ||||
|   if (fout == NULL || buffer == NULL || a == NULL) return 0; | ||||
|  | ||||
|   fprintf(fout, "P5\n%u %u\n255\n", width, height); | ||||
|   for (y = 0; y < height; ++y) { | ||||
|     if (fwrite(a + y * a_stride, width, 1, fout) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // PGM with IMC4 layout | ||||
|  | ||||
| int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const int width = buffer->width; | ||||
|   const int height = buffer->height; | ||||
|   const WebPYUVABuffer* const yuv = &buffer->u.YUVA; | ||||
|   const int uv_width = (width + 1) / 2; | ||||
|   const int uv_height = (height + 1) / 2; | ||||
|   const int a_height = (yuv->a != NULL) ? height : 0; | ||||
|   int ok = 1; | ||||
|   int y; | ||||
|  | ||||
|   if (fout == NULL || buffer == NULL) return 0; | ||||
|   if (yuv->y == NULL || yuv->u == NULL || yuv->v == NULL) return 0; | ||||
|  | ||||
|   fprintf(fout, "P5\n%d %d\n255\n", | ||||
|           (width + 1) & ~1, height + uv_height + a_height); | ||||
|   for (y = 0; ok && y < height; ++y) { | ||||
|     ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1); | ||||
|     if (width & 1) fputc(0, fout);    // padding byte | ||||
|   } | ||||
|   for (y = 0; ok && y < uv_height; ++y) { | ||||
|     ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1); | ||||
|     ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1); | ||||
|   } | ||||
|   for (y = 0; ok && y < a_height; ++y) { | ||||
|     ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1); | ||||
|     if (width & 1) fputc(0, fout);    // padding byte | ||||
|   } | ||||
|   return ok; | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Raw YUV(A) planes | ||||
|  | ||||
| int WebPWriteYUV(FILE* fout, const WebPDecBuffer* const buffer) { | ||||
|   const int width = buffer->width; | ||||
|   const int height = buffer->height; | ||||
|   const WebPYUVABuffer* const yuv = &buffer->u.YUVA; | ||||
|   const int uv_width = (width + 1) / 2; | ||||
|   const int uv_height = (height + 1) / 2; | ||||
|   const int a_height = (yuv->a != NULL) ? height : 0; | ||||
|   int ok = 1; | ||||
|   int y; | ||||
|  | ||||
|   if (fout == NULL || buffer == NULL) return 0; | ||||
|   if (yuv->y == NULL || yuv->u == NULL || yuv->v == NULL) return 0; | ||||
|  | ||||
|   for (y = 0; ok && y < height; ++y) { | ||||
|     ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1); | ||||
|   } | ||||
|   for (y = 0; ok && y < uv_height; ++y) { | ||||
|     ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1); | ||||
|   } | ||||
|   for (y = 0; ok && y < uv_height; ++y) { | ||||
|     ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1); | ||||
|   } | ||||
|   for (y = 0; ok && y < a_height; ++y) { | ||||
|     ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1); | ||||
|   } | ||||
|   return ok; | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Generic top-level call | ||||
|  | ||||
| int WebPSaveImage(const WebPDecBuffer* const buffer, | ||||
|                   WebPOutputFileFormat format, const char* const out_file) { | ||||
|   FILE* fout = NULL; | ||||
|   int needs_open_file = 1; | ||||
|   const int use_stdout = (out_file != NULL) && !strcmp(out_file, "-"); | ||||
|   int ok = 1; | ||||
|  | ||||
|   if (buffer == NULL || out_file == NULL) return 0; | ||||
|  | ||||
| #ifdef HAVE_WINCODEC_H | ||||
|   needs_open_file = (format != PNG); | ||||
| #endif | ||||
|  | ||||
|   if (needs_open_file) { | ||||
|     fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(out_file, "wb"); | ||||
|     if (fout == NULL) { | ||||
|       fprintf(stderr, "Error opening output file %s\n", out_file); | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (format == PNG || | ||||
|       format == RGBA || format == BGRA || format == ARGB || | ||||
|       format == rgbA || format == bgrA || format == Argb) { | ||||
| #ifdef HAVE_WINCODEC_H | ||||
|     ok &= WebPWritePNG(out_file, use_stdout, buffer); | ||||
| #else | ||||
|     ok &= WebPWritePNG(fout, buffer); | ||||
| #endif | ||||
|   } else if (format == PAM) { | ||||
|     ok &= WebPWritePAM(fout, buffer); | ||||
|   } else if (format == PPM || format == RGB || format == BGR) { | ||||
|     ok &= WebPWritePPM(fout, buffer); | ||||
|   } else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) { | ||||
|     ok &= WebPWrite16bAsPGM(fout, buffer); | ||||
|   } else if (format == BMP) { | ||||
|     ok &= WebPWriteBMP(fout, buffer); | ||||
|   } else if (format == TIFF) { | ||||
|     ok &= WebPWriteTIFF(fout, buffer); | ||||
|   } else if (format == RAW_YUV) { | ||||
|     ok &= WebPWriteYUV(fout, buffer); | ||||
|   } else if (format == PGM || format == YUV || format == YUVA) { | ||||
|     ok &= WebPWritePGM(fout, buffer); | ||||
|   } else if (format == ALPHA_PLANE_ONLY) { | ||||
|     ok &= WebPWriteAlphaPlane(fout, buffer); | ||||
|   } | ||||
|   if (fout != NULL && fout != stdout) { | ||||
|     fclose(fout); | ||||
|   } | ||||
|   return ok; | ||||
| } | ||||
							
								
								
									
										96
									
								
								imageio/image_enc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								imageio/image_enc.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| // Copyright 2016 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Use of this source code is governed by a BSD-style license | ||||
| // that can be found in the COPYING file in the root of the source | ||||
| // tree. An additional intellectual property rights grant can be found | ||||
| // in the file PATENTS. All contributing project authors may | ||||
| // be found in the AUTHORS file in the root of the source tree. | ||||
| // ----------------------------------------------------------------------------- | ||||
| // | ||||
| //  All-in-one library to save PNG/JPEG/WebP/TIFF/WIC images. | ||||
| // | ||||
| // Author: Skal (pascal.massimino@gmail.com) | ||||
|  | ||||
| #ifndef WEBP_IMAGEIO_IMAGE_ENC_H_ | ||||
| #define WEBP_IMAGEIO_IMAGE_ENC_H_ | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "webp/config.h" | ||||
| #endif | ||||
|  | ||||
| #include "webp/types.h" | ||||
| #include "webp/decode.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| // Output types | ||||
| typedef enum { | ||||
|   PNG = 0, | ||||
|   PAM, | ||||
|   PPM, | ||||
|   PGM, | ||||
|   BMP, | ||||
|   TIFF, | ||||
|   RAW_YUV, | ||||
|   ALPHA_PLANE_ONLY,  // this is for experimenting only | ||||
|   // forced colorspace output (for testing, mostly) | ||||
|   RGB, RGBA, BGR, BGRA, ARGB, | ||||
|   RGBA_4444, RGB_565, | ||||
|   rgbA, bgrA, Argb, rgbA_4444, | ||||
|   YUV, YUVA | ||||
| } WebPOutputFileFormat; | ||||
|  | ||||
| // General all-purpose call. | ||||
| // Most formats expect a 'buffer' containing RGBA-like samples, except | ||||
| // RAW_YUV, YUV and YUVA formats. | ||||
| // If 'out_file_name' is "-", data is saved to stdout. | ||||
| // Returns false if an error occurred, true otherwise. | ||||
| int WebPSaveImage(const WebPDecBuffer* const buffer, | ||||
|                   WebPOutputFileFormat format, const char* const out_file_name); | ||||
|  | ||||
| // Save to PNG. | ||||
| #ifdef HAVE_WINCODEC_H | ||||
| int WebPWritePNG(const char* out_file_name, int use_stdout, | ||||
|                  const struct WebPDecBuffer* const buffer); | ||||
| #else | ||||
| int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer); | ||||
| #endif | ||||
|  | ||||
| // Save to PPM format (RGB, no alpha) | ||||
| int WebPWritePPM(FILE* fout, const struct WebPDecBuffer* const buffer); | ||||
|  | ||||
| // Save to PAM format (= PPM + alpha) | ||||
| int WebPWritePAM(FILE* fout, const struct WebPDecBuffer* const buffer); | ||||
|  | ||||
| // Save 16b mode (RGBA4444, RGB565, ...) for debugging purposes. | ||||
| int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer); | ||||
|  | ||||
| // Save as BMP | ||||
| int WebPWriteBMP(FILE* fout, const struct WebPDecBuffer* const buffer); | ||||
|  | ||||
| // Save as TIFF | ||||
| int WebPWriteTIFF(FILE* fout, const struct WebPDecBuffer* const buffer); | ||||
|  | ||||
| // Save the ALPHA plane (only) as a PGM | ||||
| int WebPWriteAlphaPlane(FILE* fout, const struct WebPDecBuffer* const buffer); | ||||
|  | ||||
| // Save as YUV samples as PGM format (using IMC4 layout). | ||||
| // See: http://www.fourcc.org/yuv.php#IMC4. | ||||
| // (very convenient format for viewing the samples, esp. for odd dimensions). | ||||
| int WebPWritePGM(FILE* fout, const struct WebPDecBuffer* const buffer); | ||||
|  | ||||
| // Save YUV(A) planes sequentially (raw dump) | ||||
| int WebPWriteYUV(FILE* fout, const struct WebPDecBuffer* const buffer); | ||||
|  | ||||
| // Save 16b mode (RGBA4444, RGB565, ...) as PGM format, for debugging purposes. | ||||
| int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| }    // extern "C" | ||||
| #endif | ||||
|  | ||||
| #endif  // WEBP_IMAGEIO_IMAGE_ENC_H_ | ||||
| @@ -223,6 +223,9 @@ EX_FORMAT_DEC_OBJS = \ | ||||
|     imageio/tiffdec.o \ | ||||
|     imageio/webpdec.o \ | ||||
|  | ||||
| EX_FORMAT_ENC_OBJS = \ | ||||
|     imageio/image_enc.o \ | ||||
|  | ||||
| EX_UTIL_OBJS = \ | ||||
|     examples/example_util.o \ | ||||
|  | ||||
| @@ -311,8 +314,11 @@ HDRS = \ | ||||
|     src/webp/format_constants.h \ | ||||
|     $(HDRS_INSTALLED) \ | ||||
|  | ||||
| OUT_LIBS = examples/libexample_util.a imageio/libimageio_util.a | ||||
| OUT_LIBS += imageio/libimagedec.a src/libwebpdecoder.a | ||||
| OUT_LIBS = examples/libexample_util.a | ||||
| OUT_LIBS += imageio/libimageio_util.a | ||||
| OUT_LIBS += imageio/libimagedec.a | ||||
| OUT_LIBS += imageio/libimageenc.a | ||||
| OUT_LIBS += src/libwebpdecoder.a | ||||
| OUT_LIBS += src/libwebp.a | ||||
| EXTRA_LIB = extras/libwebpextras.a | ||||
| OUT_EXAMPLES = examples/cwebp examples/dwebp | ||||
| @@ -349,6 +355,7 @@ examples/libexample_util.a: $(EX_UTIL_OBJS) | ||||
| examples/libgifdec.a: $(GIFDEC_OBJS) | ||||
| extras/libwebpextras.a: $(LIBWEBPEXTRA_OBJS) | ||||
| imageio/libimagedec.a: $(EX_FORMAT_DEC_OBJS) | ||||
| imageio/libimageenc.a: $(EX_FORMAT_ENC_OBJS) | ||||
| imageio/libimageio_util.a: $(IMAGE_UTIL_OBJS) | ||||
| src/libwebpdecoder.a: $(LIBWEBPDECODER_OBJS) | ||||
| src/libwebp.a: $(LIBWEBP_OBJS) | ||||
| @@ -377,6 +384,7 @@ examples/cwebp: src/libwebp.a | ||||
| examples/cwebp: EXTRA_LIBS += $(CWEBP_LIBS) | ||||
| examples/dwebp: examples/libexample_util.a | ||||
| examples/dwebp: imageio/libimagedec.a | ||||
| examples/dwebp: imageio/libimageenc.a | ||||
| examples/dwebp: imageio/libimageio_util.a | ||||
| examples/dwebp: src/libwebp.a | ||||
| examples/dwebp: EXTRA_LIBS += $(DWEBP_LIBS) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user