From e4e0862c2ebe205f8c50897f15bfe256089d6ac9 Mon Sep 17 00:00:00 2001
From: "andrey.morozov" <andrey.morozov@itseez.com>
Date: Sat, 29 Jun 2013 22:00:26 +0400
Subject: [PATCH] added support image with alpha channel

---
 modules/highgui/src/grfmt_webp.cpp  | 115 +++++++++++++++++++---------
 modules/highgui/src/grfmt_webp.hpp  |   7 +-
 modules/highgui/test/test_grfmt.cpp |  10 ++-
 3 files changed, 94 insertions(+), 38 deletions(-)

diff --git a/modules/highgui/src/grfmt_webp.cpp b/modules/highgui/src/grfmt_webp.cpp
index 270262095..fd9682a79 100644
--- a/modules/highgui/src/grfmt_webp.cpp
+++ b/modules/highgui/src/grfmt_webp.cpp
@@ -46,6 +46,7 @@
 
 #include <webp/decode.h>
 #include <webp/encode.h>
+
 #include <stdio.h>
 #include <limits.h>
 
@@ -53,17 +54,38 @@
 
 #include "opencv2/imgproc.hpp"
 
+const size_t WEBP_HEADER_SIZE = 32;
+
 namespace cv
 {
 
 WebPDecoder::WebPDecoder()
 {
-    m_signature = "RIFF....WEBPVP8 ";
     m_buf_supported = true;
 }
 
-WebPDecoder::~WebPDecoder()
+WebPDecoder::~WebPDecoder() {}
+
+size_t WebPDecoder::signatureLength() const
 {
+    return WEBP_HEADER_SIZE;
+}
+
+bool WebPDecoder::checkSignature(const String & signature) const
+{
+    bool ret = false;
+
+    if(signature.size() >= WEBP_HEADER_SIZE)
+    {
+        WebPBitstreamFeatures features;
+        if(VP8_STATUS_OK == WebPGetFeatures((uint8_t *)signature.c_str(),
+                                            WEBP_HEADER_SIZE, &features))
+        {
+            ret = true;
+        }
+    }
+
+    return ret;
 }
 
 ImageDecoder WebPDecoder::newDecoder() const
@@ -71,20 +93,6 @@ ImageDecoder WebPDecoder::newDecoder() const
     return new WebPDecoder;
 }
 
-bool WebPDecoder::checkSignature( const String& signature ) const
-{
-    size_t len = signatureLength();
-    bool ret = false;
-
-    if(signature.size() >= len)
-    {
-        ret = ( (memcmp(signature.c_str(), m_signature.c_str(), 4) == 0) &&
-            (memcmp(signature.c_str() + 8, m_signature.c_str() + 8, 4) == 0) );
-    }
-
-    return ret;
-}
-
 bool WebPDecoder::readHeader()
 {
     if (m_buf.empty())
@@ -99,16 +107,16 @@ bool WebPDecoder::readHeader()
         }
 
         fseek(wfile, 0, SEEK_END);
-        size_t wfile_size = ftell(wfile);
+        long int wfile_size = ftell(wfile);
         fseek(wfile, 0, SEEK_SET);
 
-        if(wfile_size > (size_t)INT_MAX)
+        if(wfile_size > static_cast<long int>(INT_MAX))
         {
             fclose(wfile);
             return false;
         }
 
-        data.create(1, (int)wfile_size, CV_8U);
+        data.create(1, wfile_size, CV_8U);
 
         size_t data_size = fread(data.data, 1, wfile_size, wfile);
 
@@ -117,7 +125,7 @@ bool WebPDecoder::readHeader()
             fclose(wfile);
         }
 
-        if( data_size < wfile_size )
+        if(static_cast<long int>(data_size) != wfile_size)
         {
             return false;
         }
@@ -127,9 +135,23 @@ bool WebPDecoder::readHeader()
         data = m_buf;
     }
 
-    if(WebPGetInfo(data.data, data.total(), &m_width, &m_height) == 1)
+    WebPBitstreamFeatures features;
+    if(VP8_STATUS_OK == WebPGetFeatures(data.data, WEBP_HEADER_SIZE, &features))
     {
-        m_type = CV_8UC3;
+        m_width  = features.width;
+        m_height = features.height;
+
+        if (features.has_alpha)
+        {
+            m_type = CV_8UC4;
+            channels = 4;
+        }
+        else
+        {
+            m_type = CV_8UC3;
+            channels = 3;
+        }
+
         return true;
     }
 
@@ -140,17 +162,24 @@ bool WebPDecoder::readData(Mat &img)
 {
     if( m_width > 0 && m_height > 0 )
     {
+        if (img.cols != m_width || img.rows != m_height || img.type() != m_type)
+        {
+            img.create(m_height, m_width, m_type);
+        }
+
         uchar* out_data = img.data;
-        unsigned int out_data_size = m_width * m_height * 3 * sizeof(uchar);
+        size_t out_data_size = img.cols * img.rows * img.elemSize();
 
         uchar *res_ptr = 0;
         if (channels == 3)
         {
-            res_ptr = WebPDecodeBGRInto(data.data, data.total(), out_data, out_data_size, img.step);
+            res_ptr = WebPDecodeBGRInto(data.data, data.total(), out_data,
+                                        out_data_size, img.step);
         }
         else if (channels == 4)
         {
-            res_ptr = WebPDecodeBGRAInto(data.data, data.total(), out_data, out_data_size, img.step);
+            res_ptr = WebPDecodeBGRAInto(data.data, data.total(), out_data,
+                                         out_data_size, img.step);
         }
 
         if(res_ptr == out_data)
@@ -168,9 +197,7 @@ WebPEncoder::WebPEncoder()
     m_buf_supported = true;
 }
 
-WebPEncoder::~WebPEncoder()
-{
-}
+WebPEncoder::~WebPEncoder() { }
 
 ImageEncoder WebPEncoder::newEncoder() const
 {
@@ -187,19 +214,19 @@ bool WebPEncoder::write(const Mat& img, const std::vector<int>& params)
     size_t size = 0;
 
     bool comp_lossless = true;
-    int quality = 100;
+    float quality = 100.0f;
 
     if (params.size() > 1)
     {
         if (params[0] == CV_IMWRITE_WEBP_QUALITY)
         {
             comp_lossless = false;
-            quality = params[1];
-            if (quality < 1)
+            quality = static_cast<float>(params[1]);
+            if (quality < 1.0f)
             {
-                quality = 1;
+                quality = 1.0f;
             }
-            if (quality > 100)
+            if (quality > 100.0f)
             {
                 comp_lossless = true;
             }
@@ -219,14 +246,32 @@ bool WebPEncoder::write(const Mat& img, const std::vector<int>& params)
         image = &temp;
         channels = 3;
     }
+    else if (channels == 2)
+    {
+        return false;
+    }
 
     if (comp_lossless)
     {
-        size = WebPEncodeLosslessBGR(image->data, width, height, ((width * 3 + 3) & ~3), &out);
+        if(channels == 3)
+        {
+            size = WebPEncodeLosslessBGR(image->data, width, height, image->step, &out);
+        }
+        else if(channels == 4)
+        {
+            size = WebPEncodeLosslessBGRA(image->data, width, height, image->step, &out);
+        }
     }
     else
     {
-        size = WebPEncodeBGR(image->data, width, height, ((width * 3 + 3) & ~3), (float)quality, &out);
+        if(channels == 3)
+        {
+            size = WebPEncodeBGR(image->data, width, height, image->step, quality, &out);
+        }
+        else if(channels == 4)
+        {
+            size = WebPEncodeBGRA(image->data, width, height, image->step, quality, &out);
+        }
     }
 
     if(size > 0)
diff --git a/modules/highgui/src/grfmt_webp.hpp b/modules/highgui/src/grfmt_webp.hpp
index f37e6e9f4..ea692bf8d 100644
--- a/modules/highgui/src/grfmt_webp.hpp
+++ b/modules/highgui/src/grfmt_webp.hpp
@@ -47,6 +47,8 @@
 
 #ifdef HAVE_WEBP
 
+
+
 namespace cv
 {
 
@@ -60,12 +62,15 @@ public:
     bool readData( Mat& img );
     bool readHeader();
     void close();
-    bool checkSignature( const String& signature ) const;
+
+    size_t signatureLength() const;
+    bool checkSignature( const String& signature) const;
 
     ImageDecoder newDecoder() const;
 
 protected:
     Mat data;
+    int channels;
 };
 
 class WebPEncoder : public BaseImageEncoder
diff --git a/modules/highgui/test/test_grfmt.cpp b/modules/highgui/test/test_grfmt.cpp
index d4e18dd2d..28af633da 100644
--- a/modules/highgui/test/test_grfmt.cpp
+++ b/modules/highgui/test/test_grfmt.cpp
@@ -387,7 +387,7 @@ TEST(Highgui_WebP, encode_decode_lossless_webp)
 TEST(Highgui_WebP, encode_decode_lossy_webp)
 {
     cvtest::TS& ts = *cvtest::TS::ptr();
-    std::string input = std::string(ts.get_data_path()) + "/../cv/shared/lena.png";
+    std::string input = std::string(ts.get_data_path()) + "../cv/shared/lena.png";
     cv::Mat img = cv::imread(input);
     ASSERT_FALSE(img.empty());
 
@@ -402,13 +402,16 @@ TEST(Highgui_WebP, encode_decode_lossy_webp)
         cv::Mat img_webp = cv::imread(output);
         remove(output.c_str());
         EXPECT_FALSE(img_webp.empty());
+        EXPECT_EQ(3,   img_webp.channels());
+        EXPECT_EQ(512, img_webp.cols);
+        EXPECT_EQ(512, img_webp.rows);
     }
 }
 
 TEST(Highgui_WebP, encode_decode_with_alpha_webp)
 {
     cvtest::TS& ts = *cvtest::TS::ptr();
-    std::string input = std::string(ts.get_data_path()) + "/../cv/shared/lena.png";
+    std::string input = std::string(ts.get_data_path()) + "../cv/shared/lena.png";
     cv::Mat img = cv::imread(input);
     ASSERT_FALSE(img.empty());
 
@@ -424,6 +427,9 @@ TEST(Highgui_WebP, encode_decode_with_alpha_webp)
     cv::Mat img_webp = cv::imread(output);
     remove(output.c_str());
     EXPECT_FALSE(img_webp.empty());
+    EXPECT_EQ(4,   img_webp.channels());
+    EXPECT_EQ(512, img_webp.cols);
+    EXPECT_EQ(512, img_webp.rows);
 }
 
 #endif