Implement scaling in SkCodecImageGenerator
authorLeon Scroggins III <scroggo@google.com>
Mon, 19 Dec 2016 20:04:06 +0000 (15:04 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Wed, 21 Dec 2016 19:12:51 +0000 (19:12 +0000)
Plumb calls down to SkCodec.

Add a gm

Change-Id: I16da24eb739295ab72f487df02f19968151443f3
Reviewed-on: https://skia-review.googlesource.com/6287
Reviewed-by: Matt Sarett <msarett@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>

gm/codec_scaled.cpp [new file with mode: 0644]
gn/gm.gni
src/codec/SkCodecImageGenerator.cpp
src/codec/SkCodecImageGenerator.h

diff --git a/gm/codec_scaled.cpp b/gm/codec_scaled.cpp
new file mode 100644 (file)
index 0000000..270de5d
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkCodec.h"
+#include "SkCodecImageGenerator.h"
+#include "SkColor.h"
+#include "SkCommandLineFlags.h"
+#include "SkImageGenerator.h"
+#include "SkString.h"
+#include "Resources.h"
+
+DEFINE_string(codec_scaled, "brickwork-texture.jpg", "Image in resources/ to draw scaled.");
+
+class CodecScaledGM : public skiagm::GM {
+private:
+    // FIXME: Once generateScaledPixels is plumbed to SkImage, store an SkImage
+    // and call SkImage::scalePixels.
+    std::unique_ptr<SkImageGenerator>   fGenerator;
+
+public:
+    CodecScaledGM()
+    {}
+
+private:
+    SkString onShortName() override {
+        return SkString("codec_scaled");
+    }
+
+    SkISize onISize() override {
+        if (this->initCodec()) {
+            SkISize dim = fGenerator->getInfo().dimensions();
+            // Wide enough to show 8 versions, corresponding to the options JPEG supports.
+            dim.fWidth *= 8;
+            // Tall enough to display 2 versions - one using computed dimensions, and one
+            // with scaling.
+            dim.fHeight *= 2;
+            return dim;
+        }
+        return SkISize::Make(640, 480);
+    }
+
+    void onDrawBackground(SkCanvas* canvas) override {
+        canvas->clear(SK_ColorWHITE);
+    }
+
+    bool initCodec() {
+        if (fGenerator) {
+            return true;
+        }
+
+        if (FLAGS_codec_scaled.isEmpty()) {
+            SkDebugf("Nothing specified for --codec_scaled!");
+            return false;
+        }
+
+        SkString path = GetResourcePath(FLAGS_codec_scaled[0]);
+        sk_sp<SkData> data(SkData::MakeFromFileName(path.c_str()));
+        if (!data) {
+            return false;
+        }
+
+        fGenerator.reset(SkCodecImageGenerator::NewFromEncodedCodec(data));
+        if (!fGenerator) {
+            SkDebugf("Could create codec from %s", FLAGS_codec_scaled[0]);
+            return false;
+        }
+
+        return true;
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        if (!this->initCodec()) {
+            return;
+        }
+
+        SkPMColor colorStorage[256];
+        int colorCount = 256;
+        sk_sp<SkColorTable> ctable(new SkColorTable(colorStorage, colorCount));
+
+        SkAutoCanvasRestore acr(canvas, true);
+        for (float scale : { 1.0f, .875f, .750f, .625f, .5f, .375f, .25f, .125f }) {
+            const auto info = fGenerator->getInfo();
+            auto scaledInfo = info;
+            SkImageGenerator::SupportedSizes sizes;
+            if (fGenerator->computeScaledDimensions(scale, &sizes)) {
+                scaledInfo = info.makeWH(sizes.fSizes[0].fWidth, sizes.fSizes[0].fHeight);
+            }
+
+            SkBitmap bm;
+            bm.setInfo(scaledInfo);
+            SkColorTable* ctablePtr = info.colorType() == kIndex_8_SkColorType ? ctable.get()
+                                                                               : nullptr;
+            bm.allocPixels(ctablePtr);
+            SkPixmap pixmap(scaledInfo, bm.getPixels(), bm.rowBytes(), ctablePtr);
+            if (fGenerator->generateScaledPixels(pixmap)) {
+                // If there's a color table, we need to use it.
+                SkBitmap bm2;
+                bm2.installPixels(pixmap);
+                canvas->drawBitmap(bm2, 0, 0);
+            }
+
+            bm.setInfo(info);
+            bm.allocPixels(ctablePtr);
+            colorCount = 256;
+            if (fGenerator->getPixels(info, bm.getPixels(), bm.rowBytes(),
+                    const_cast<SkPMColor*>(ctable->readColors()), &colorCount)) {
+                SkAutoCanvasRestore acr2(canvas, true);
+                canvas->translate(0, SkIntToScalar(info.height()));
+                canvas->scale(SkFloatToScalar(scale), SkFloatToScalar(scale));
+                canvas->drawBitmap(bm, 0, 0);
+            }
+
+            canvas->translate(SkIntToScalar(info.width()), 0);
+        }
+    }
+};
+
+DEF_GM(return new CodecScaledGM);
index a8e0eb4..8beb3e9 100644 (file)
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -51,6 +51,7 @@ gm_sources = [
   "$_gm/bug615686.cpp",
   "$_gm/cgm.c",
   "$_gm/cgms.cpp",
+  "$_gm/codec_scaled.cpp",
   "$_gm/circles.cpp",
   "$_gm/circulararcs.cpp",
   "$_gm/circularclips.cpp",
index 447332b..fcbd7c3 100644 (file)
@@ -47,6 +47,43 @@ bool SkCodecImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, s
     }
 }
 
+bool SkCodecImageGenerator::onComputeScaledDimensions(SkScalar scale, SupportedSizes* sizes) {
+    SkASSERT(scale > 0 && scale <= 1);
+    const auto size = fCodec->getScaledDimensions(SkScalarToFloat(scale));
+    if (size == this->getInfo().dimensions()) {
+        return false;
+    }
+
+    // FIXME: Make SkCodec's API return two potential sizes, like this one. For now, set them both
+    // to be the same.
+    sizes->fSizes[0] = sizes->fSizes[1] = size;
+    return true;
+}
+
+bool SkCodecImageGenerator::onGenerateScaledPixels(const SkPixmap& pixmap) {
+    SkPMColor colorStorage[256];
+    int colorCount = 256;
+    const auto result = fCodec->getPixels(pixmap.info(), pixmap.writable_addr(),
+                                          pixmap.rowBytes(), nullptr, colorStorage, &colorCount);
+    switch (result) {
+        case SkCodec::kSuccess:
+        case SkCodec::kIncompleteInput:
+            break;
+        default:
+            return false;
+    }
+
+    if (pixmap.colorType() == kIndex_8_SkColorType) {
+        // SkPixmap does not take ownership, so we need to hang onto this.
+        // FIXME: With a better API on SkCodec, the SkCodec could share its SkColorTable.
+        fColorTable.reset(new SkColorTable(colorStorage, colorCount));
+        const_cast<SkPixmap&>(pixmap).reset(pixmap.info(), pixmap.addr(), pixmap.rowBytes(),
+                                            fColorTable.get());
+    }
+    return true;
+}
+
+
 bool SkCodecImageGenerator::onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const
 {
     return fCodec->queryYUV8(sizeInfo, colorSpace);
index 222923b..e1aa220 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "SkCodec.h"
+#include "SkColorTable.h"
 #include "SkData.h"
 #include "SkImageGenerator.h"
 
@@ -30,6 +31,10 @@ protected:
 
     bool onGetYUV8Planes(const SkYUVSizeInfo&, void* planes[3]) override;
 
+    bool onComputeScaledDimensions(SkScalar, SupportedSizes*) override;
+
+    bool onGenerateScaledPixels(const SkPixmap&) override;
+
 private:
     /*
      * Takes ownership of codec
@@ -38,6 +43,7 @@ private:
 
     std::unique_ptr<SkCodec> fCodec;
     sk_sp<SkData> fData;
+    sk_sp<SkColorTable> fColorTable;
 
     typedef SkImageGenerator INHERITED;
 };