windowscodecs: Fix interlaced PNG writing.

Vincent Povirk madewokherd at gmail.com
Fri Jun 20 15:15:01 CDT 2014


For bug 36749.
-------------- next part --------------
From 4fabb0e4ac3d13ffbffe0f8c2a6b61570630d767 Mon Sep 17 00:00:00 2001
From: Vincent Povirk <vincent at codeweavers.com>
Date: Fri, 20 Jun 2014 15:11:01 -0500
Subject: [PATCH] windowscodecs: Fix interlaced PNG writing.

---
 dlls/windowscodecs/pngformat.c       | 55 ++++++++++++++++++++++++++++++++++
 dlls/windowscodecs/tests/converter.c | 57 +++++++++++++++++++++++++++++++-----
 2 files changed, 104 insertions(+), 8 deletions(-)

diff --git a/dlls/windowscodecs/pngformat.c b/dlls/windowscodecs/pngformat.c
index 6950c07..fda14d0 100644
--- a/dlls/windowscodecs/pngformat.c
+++ b/dlls/windowscodecs/pngformat.c
@@ -185,6 +185,7 @@ MAKE_FUNCPTR(png_set_gray_1_2_4_to_8);
 #endif
 MAKE_FUNCPTR(png_set_filler);
 MAKE_FUNCPTR(png_set_gray_to_rgb);
+MAKE_FUNCPTR(png_set_interlace_handling);
 MAKE_FUNCPTR(png_set_IHDR);
 MAKE_FUNCPTR(png_set_pHYs);
 MAKE_FUNCPTR(png_set_read_fn);
@@ -234,6 +235,7 @@ static void *load_libpng(void)
 #endif
         LOAD_FUNCPTR(png_set_filler);
         LOAD_FUNCPTR(png_set_gray_to_rgb);
+        LOAD_FUNCPTR(png_set_interlace_handling);
         LOAD_FUNCPTR(png_set_IHDR);
         LOAD_FUNCPTR(png_set_pHYs);
         LOAD_FUNCPTR(png_set_read_fn);
@@ -1063,6 +1065,9 @@ typedef struct PngEncoder {
     BOOL committed;
     CRITICAL_SECTION lock;
     BOOL interlace;
+    BYTE *data;
+    UINT stride;
+    UINT passes;
 } PngEncoder;
 
 static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
@@ -1287,6 +1292,18 @@ static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
 
     if (!This->info_written)
     {
+        if (This->interlace)
+        {
+            /* libpng requires us to write all data multiple times in this case. */
+            This->stride = (This->format->bpp * This->width + 7)/8;
+            This->data = HeapAlloc(GetProcessHeap(), 0, This->height * This->stride);
+            if (!This->data)
+            {
+                LeaveCriticalSection(&This->lock);
+                return E_OUTOFMEMORY;
+            }
+        }
+
         ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height,
             This->format->bit_depth, This->format->color_type,
             This->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
@@ -1306,9 +1323,26 @@ static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
         if (This->format->swap_rgb)
             ppng_set_bgr(This->png_ptr);
 
+        if (This->interlace)
+            This->passes = ppng_set_interlace_handling(This->png_ptr);
+
         This->info_written = TRUE;
     }
 
+    if (This->interlace)
+    {
+        /* Just store the data so we can write it in multiple passes in Commit. */
+        for (i=0; i<lineCount; i++)
+            memcpy(This->data + This->stride * (This->lines_written + i),
+                   pbPixels + cbStride * i,
+                   This->stride);
+
+        This->lines_written += lineCount;
+
+        LeaveCriticalSection(&This->lock);
+        return S_OK;
+    }
+
     row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*));
     if (!row_pointers)
     {
@@ -1374,6 +1408,25 @@ static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface)
     }
     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
 
+    if (This->interlace)
+    {
+        png_byte **row_pointers=NULL;
+        int i;
+
+        row_pointers = HeapAlloc(GetProcessHeap(), 0, This->height * sizeof(png_byte*));
+        if (!row_pointers)
+        {
+            LeaveCriticalSection(&This->lock);
+            return E_OUTOFMEMORY;
+        }
+
+        for (i=0; i<This->height; i++)
+            row_pointers[i] = This->data + This->stride * i;
+
+        for (i=0; i<This->passes; i++)
+            ppng_write_rows(This->png_ptr, row_pointers, This->height);
+    }
+
     ppng_write_end(This->png_ptr, This->info_ptr);
 
     This->frame_committed = TRUE;
@@ -1455,6 +1508,7 @@ static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface)
             ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
         if (This->stream)
             IStream_Release(This->stream);
+        HeapFree(GetProcessHeap(), 0, This->data);
         HeapFree(GetProcessHeap(), 0, This);
     }
 
@@ -1695,6 +1749,7 @@ HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
     This->lines_written = 0;
     This->frame_committed = FALSE;
     This->committed = FALSE;
+    This->data = NULL;
     InitializeCriticalSection(&This->lock);
     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock");
 
diff --git a/dlls/windowscodecs/tests/converter.c b/dlls/windowscodecs/tests/converter.c
index 095298f..5c764d2 100644
--- a/dlls/windowscodecs/tests/converter.c
+++ b/dlls/windowscodecs/tests/converter.c
@@ -378,6 +378,7 @@ typedef struct property_opt_test_data
 
 static const WCHAR wszTiffCompressionMethod[] = {'T','i','f','f','C','o','m','p','r','e','s','s','i','o','n','M','e','t','h','o','d',0};
 static const WCHAR wszCompressionQuality[] = {'C','o','m','p','r','e','s','s','i','o','n','Q','u','a','l','i','t','y',0};
+static const WCHAR wszInterlaceOption[] = {'I','n','t','e','r','l','a','c','e','O','p','t','i','o','n',0};
 
 static const struct property_opt_test_data testdata_tiff_props[] = {
     { wszTiffCompressionMethod, VT_UI1,         VT_UI1,  WICTiffCompressionDontCare },
@@ -508,8 +509,16 @@ static void test_encoder_properties(const CLSID* clsid_encoder, IPropertyBag2 *o
     }
 }
 
+struct setting {
+    const WCHAR *name;
+    PROPBAG2_TYPE type;
+    VARTYPE vt;
+    void *value;
+};
+
 static void test_multi_encoder(const struct bitmap_data **srcs, const CLSID* clsid_encoder,
-    const struct bitmap_data **dsts, const CLSID *clsid_decoder, WICRect *rc, const char *name)
+    const struct bitmap_data **dsts, const CLSID *clsid_decoder, WICRect *rc,
+    const struct setting *settings, const char *name)
 {
     HRESULT hr;
     IWICBitmapEncoder *encoder;
@@ -554,6 +563,26 @@ static void test_multi_encoder(const struct bitmap_data **srcs, const CLSID* cls
                     if(options)
                         test_encoder_properties(clsid_encoder, options);
 
+                    if (settings)
+                    {
+                        int j;
+                        for (j=0; settings[j].name; j++)
+                        {
+                            PROPBAG2 propbag;
+                            VARIANT var;
+
+                            memset(&propbag, 0, sizeof(propbag));
+                            memset(&var, 0, sizeof(var));
+                            propbag.pstrName = (LPOLESTR)settings[j].name;
+                            propbag.dwType = settings[j].type;
+                            V_VT(&var) = settings[j].vt;
+                            V_UNKNOWN(&var) = settings[j].value;
+
+                            hr = IPropertyBag2_Write(options, 1, &propbag, &var);
+                            ok(SUCCEEDED(hr), "Writing property %s failed, hr=%x\n", wine_dbgstr_w(settings[j].name), hr);
+                        }
+                    }
+
                     hr = IWICBitmapFrameEncode_Initialize(frameencode, options);
                     ok(SUCCEEDED(hr), "Initialize failed, hr=%x\n", hr);
 
@@ -641,7 +670,7 @@ static void test_encoder(const struct bitmap_data *src, const CLSID* clsid_encod
     dsts[0] = dst;
     dsts[1] = NULL;
 
-    test_multi_encoder(srcs, clsid_encoder, dsts, clsid_decoder, NULL, name);
+    test_multi_encoder(srcs, clsid_encoder, dsts, clsid_decoder, NULL, NULL, name);
 }
 
 static void test_encoder_rects(void)
@@ -660,20 +689,20 @@ static void test_encoder_rects(void)
     rc.Width = 4;
     rc.Height = 2;
 
-    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, "test_encoder_rects full");
+    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, NULL, "test_encoder_rects full");
 
     rc.Width = 0;
-    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, "test_encoder_rects width=0");
+    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, NULL, "test_encoder_rects width=0");
 
     rc.Width = -1;
-    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, "test_encoder_rects width=-1");
+    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, NULL, "test_encoder_rects width=-1");
 
     rc.Width = 4;
     rc.Height = 0;
-    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, "test_encoder_rects height=0");
+    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, NULL, "test_encoder_rects height=0");
 
     rc.Height = -1;
-    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, "test_encoder_rects height=-1");
+    test_multi_encoder(srcs, &CLSID_WICTiffEncoder, dsts, &CLSID_WICTiffDecoder, &rc, NULL, "test_encoder_rects height=-1");
 }
 
 static const struct bitmap_data *multiple_frames[3] = {
@@ -681,6 +710,15 @@ static const struct bitmap_data *multiple_frames[3] = {
     &testdata_24bppBGR,
     NULL};
 
+static const struct bitmap_data *single_frame[2] = {
+    &testdata_24bppBGR,
+    NULL};
+
+static const struct setting png_interlace_settings[] = {
+    {wszInterlaceOption, PROPBAG2_TYPE_DATA, VT_BOOL, (void*)VARIANT_TRUE},
+    {NULL}
+};
+
 START_TEST(converter)
 {
     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
@@ -711,9 +749,12 @@ START_TEST(converter)
                  &testdata_24bppBGR, &CLSID_WICTiffDecoder, "TIFF encoder 24bppBGR");
 
     test_multi_encoder(multiple_frames, &CLSID_WICTiffEncoder,
-                       multiple_frames, &CLSID_WICTiffDecoder, NULL, "TIFF encoder multi-frame");
+                       multiple_frames, &CLSID_WICTiffDecoder, NULL, NULL, "TIFF encoder multi-frame");
 
     test_encoder_rects();
 
+    test_multi_encoder(single_frame, &CLSID_WICPngEncoder,
+                       single_frame, &CLSID_WICPngDecoder, NULL, png_interlace_settings, "PNG encoder interlaced");
+
     CoUninitialize();
 }
-- 
1.8.3.2



More information about the wine-patches mailing list