Add support for writing ICC profiles to webp encoder
authorMatt Sarett <msarett@google.com>
Wed, 5 Apr 2017 20:52:00 +0000 (16:52 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Wed, 5 Apr 2017 21:33:14 +0000 (21:33 +0000)
Bug: skia:
Change-Id: If0a8f84ed88da96924370b841f2283c0ff8e32ab
Reviewed-on: https://skia-review.googlesource.com/10212
Commit-Queue: Matt Sarett <msarett@google.com>
Reviewed-by: Leon Scroggins <scroggo@google.com>
src/images/SkWEBPImageEncoder.cpp
tests/CodecTest.cpp
third_party/libwebp/BUILD.gn

index 28dff0569ce67d9adf8b71a073ef79e82bb182e3..728dbc712a164039b8c7a3ebb07b504c4cf9f1dc 100644 (file)
@@ -26,7 +26,7 @@
 #include "SkUnPreMultiply.h"
 #include "SkUtils.h"
 
-// A WebP decoder only, on top of (subset of) libwebp
+// A WebP encoder only, on top of (subset of) libwebp
 // For more information on WebP image format, and libwebp library, see:
 //   http://code.google.com/speed/webp/
 //   http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
@@ -37,6 +37,7 @@ extern "C" {
 // If moving libwebp out of skia source tree, path for webp headers must be
 // updated accordingly. Here, we enforce using local copy in webp sub-directory.
 #include "webp/encode.h"
+#include "webp/mux.h"
 }
 
 static transform_scanline_proc choose_proc(const SkImageInfo& info) {
@@ -174,10 +175,17 @@ static bool do_encode(SkWStream* stream, const SkPixmap& srcPixmap, const SkEnco
 
     WebPPicture pic;
     WebPPictureInit(&pic);
+    SkAutoTCallVProc<WebPPicture, WebPPictureFree> autoPic(&pic);
     pic.width = pixmap.width();
     pic.height = pixmap.height();
     pic.writer = stream_writer;
-    pic.custom_ptr = (void*)stream;
+
+    // If there is no need to embed an ICC profile, we write directly to the input stream.
+    // Otherwise, we will first encode to |tmp| and use a mux to add the ICC chunk.  libwebp
+    // forces us to have an encoded image before we can add a profile.
+    sk_sp<SkData> icc = pixmap.colorSpace() ? icc_from_color_space(*pixmap.colorSpace()) : nullptr;
+    SkDynamicMemoryWStream tmp;
+    pic.custom_ptr = icc ? (void*)&tmp : (void*)stream;
 
     const uint8_t* src = (uint8_t*)pixmap.addr();
     const int rgbStride = pic.width * bpp;
@@ -190,21 +198,47 @@ static bool do_encode(SkWStream* stream, const SkPixmap& srcPixmap, const SkEnco
         proc((char*) &rgb[y * rgbStride], (const char*) &src[y * rowBytes], pic.width, bpp, colors);
     }
 
-    bool ok;
-    if (bpp == 3) {
-        ok = SkToBool(WebPPictureImportRGB(&pic, &rgb[0], rgbStride));
-    } else {
+    auto importProc = WebPPictureImportRGB;
+    if (3 != bpp) {
         if (pixmap.isOpaque()) {
-            ok = SkToBool(WebPPictureImportRGBX(&pic, &rgb[0], rgbStride));
+            importProc = WebPPictureImportRGBX;
         } else {
-            ok = SkToBool(WebPPictureImportRGBA(&pic, &rgb[0], rgbStride));
+            importProc = WebPPictureImportRGBA;
         }
     }
 
-    ok = ok && WebPEncode(&webp_config, &pic);
-    WebPPictureFree(&pic);
+    if (!importProc(&pic, &rgb[0], rgbStride)) {
+        return false;
+    }
+
+    if (!WebPEncode(&webp_config, &pic)) {
+        return false;
+    }
+
+    if (icc) {
+        sk_sp<SkData> encodedData = tmp.detachAsData();
+        WebPData encoded = { encodedData->bytes(), encodedData->size() };
+        WebPData iccChunk = { icc->bytes(), icc->size() };
+
+        SkAutoTCallVProc<WebPMux, WebPMuxDelete> mux(WebPMuxNew());
+        if (WEBP_MUX_OK != WebPMuxSetImage(mux, &encoded, 0)) {
+            return false;
+        }
+
+        if (WEBP_MUX_OK != WebPMuxSetChunk(mux, "ICCP", &iccChunk, 0)) {
+            return false;
+        }
+
+        WebPData assembled;
+        if (WEBP_MUX_OK != WebPMuxAssemble(mux, &assembled)) {
+            return false;
+        }
+
+        stream->write(assembled.bytes, assembled.size);
+        WebPDataClear(&assembled);
+    }
 
-    return ok;
+    return true;
 }
 
 bool SkEncodeImageAsWEBP(SkWStream* stream, const SkPixmap& src, int quality) {
index f3a393d7ac61032206df8421248a37aa8374c8ef..3897878879436205871c0df2141d4d714ad6a0c3 100644 (file)
@@ -1538,6 +1538,9 @@ static void encode_format(SkDynamicMemoryWStream* stream, const SkPixmap& pixmap
         case SkEncodedImageFormat::kJPEG:
             SkEncodeImageAsJPEG(stream, pixmap, opts);
             break;
+        case SkEncodedImageFormat::kWEBP:
+            SkEncodeImageAsWEBP(stream, pixmap, opts);
+            break;
         default:
             SkASSERT(false);
             break;
@@ -1586,4 +1589,5 @@ static void test_encode_icc(skiatest::Reporter* r, SkEncodedImageFormat format)
 DEF_TEST(Codec_EncodeICC, r) {
     test_encode_icc(r, SkEncodedImageFormat::kPNG);
     test_encode_icc(r, SkEncodedImageFormat::kJPEG);
+    test_encode_icc(r, SkEncodedImageFormat::kWEBP);
 }
index 3e9bd060209e8b87158e2468f59b960fa88c027b..7fec982e357c951356d987383a629b364f639c72 100644 (file)
@@ -128,6 +128,10 @@ if (skia_use_system_libwebp) {
       "../externals/libwebp/src/enc/tree_enc.c",
       "../externals/libwebp/src/enc/vp8l_enc.c",
       "../externals/libwebp/src/enc/webp_enc.c",
+      "../externals/libwebp/src/mux/anim_encode.c",
+      "../externals/libwebp/src/mux/muxedit.c",
+      "../externals/libwebp/src/mux/muxinternal.c",
+      "../externals/libwebp/src/mux/muxread.c",
       "../externals/libwebp/src/utils/bit_reader_utils.c",
       "../externals/libwebp/src/utils/bit_writer_utils.c",
       "../externals/libwebp/src/utils/color_cache_utils.c",