From fa15877f487333bec876e7315cf584c0d598d098 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Mon, 16 Oct 2017 11:42:53 -0400 Subject: [PATCH] apply codec origin in generator Bug: skia: Change-Id: I383dacb49b1e3c88467ccdbf3288764bb1bbf01a Reviewed-on: https://skia-review.googlesource.com/58600 Reviewed-by: Leon Scroggins Commit-Queue: Mike Reed --- gm/encode.cpp | 26 +++++++++ include/codec/SkCodec.h | 5 ++ src/codec/SkCodecImageGenerator.cpp | 74 +++++++++++++++++++++++--- src/core/SkPixmap.cpp | 82 ++++++++++++++++++++++++++++- src/core/SkPixmapPriv.h | 33 ++++++++++++ 5 files changed, 211 insertions(+), 9 deletions(-) create mode 100644 src/core/SkPixmapPriv.h diff --git a/gm/encode.cpp b/gm/encode.cpp index bf13d3dc09..88e7724930 100644 --- a/gm/encode.cpp +++ b/gm/encode.cpp @@ -48,3 +48,29 @@ private: DEF_GM( return new EncodeGM; ) } + +/////////// + +#if 0 +DEF_SIMPLE_GM(jpeg_orientation, canvas, 1000, 1000) { + static sk_sp imgs[8]; + if (!imgs[0]) { + for (int i = 0; i < 8; ++i) { + SkString path; + path.printf("/skia/orientation/Landscape_%d.jpg", i + 1); + auto stream = SkStream::MakeFromFile(path.c_str()); + auto data = SkData::MakeFromStream(stream.get(), stream->getLength()); + imgs[i] = SkImage::MakeFromEncoded(data, nullptr); + } + } + canvas->scale(0.25, 0.25); + for (int i = 0; i < 8; ++i) { + SkImage* img = imgs[i].get(); + canvas->drawImage(img, 0, 0, nullptr); + canvas->translate(0, img->height()); + if (i == 3) { + canvas->translate(img->width(), -4*img->height()); + } + } +} +#endif diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h index e2596890f3..6e9c8280b9 100644 --- a/include/codec/SkCodec.h +++ b/include/codec/SkCodec.h @@ -15,6 +15,7 @@ #include "SkEncodedImageFormat.h" #include "SkEncodedInfo.h" #include "SkImageInfo.h" +#include "SkPixmap.h" #include "SkSize.h" #include "SkStream.h" #include "SkTypes.h" @@ -355,6 +356,10 @@ public: return this->getPixels(info, pixels, rowBytes, nullptr); } + Result getPixels(const SkPixmap& pm, const Options* opts = nullptr) { + return this->getPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), opts); + } + /** * If decoding to YUV is supported, this returns true. Otherwise, this * returns false and does not modify any of the parameters. diff --git a/src/codec/SkCodecImageGenerator.cpp b/src/codec/SkCodecImageGenerator.cpp index e6ca2122ea..e8d7d3038a 100644 --- a/src/codec/SkCodecImageGenerator.cpp +++ b/src/codec/SkCodecImageGenerator.cpp @@ -5,8 +5,41 @@ * found in the LICENSE file. */ +#include "SkAutoPixmapStorage.h" #include "SkCodecImageGenerator.h" #include "SkMakeUnique.h" +#include "SkPixmapPriv.h" + +#define kMirrorX SkPixmapPriv::kMirrorX +#define kMirrorY SkPixmapPriv::kMirrorY +#define kSwapXY SkPixmapPriv::kSwapXY + +const uint8_t gOrientationFlags[] = { + 0, // kTopLeft_Origin + kMirrorX, // kTopRight_Origin + kMirrorX | kMirrorY, // kBottomRight_Origin + kMirrorY, // kBottomLeft_Origin + kSwapXY, // kLeftTop_Origin + kMirrorX | kSwapXY, // kRightTop_Origin + kMirrorX | kMirrorY | kSwapXY, // kRightBottom_Origin + kMirrorY | kSwapXY, // kLeftBottom_Origin +}; + +SkPixmapPriv::OrientFlags SkPixmapPriv::OriginToOrient(SkCodec::Origin o) { + unsigned io = static_cast(o) - 1; + SkASSERT(io < SK_ARRAY_COUNT(gOrientationFlags)); + return static_cast(gOrientationFlags[io]); +} + +static bool should_swap_width_height(SkCodec::Origin o) { + return SkToBool(SkPixmapPriv::OriginToOrient(o) & kSwapXY); +} + +static SkImageInfo swap_width_height(SkImageInfo info) { + return info.makeWH(info.height(), info.width()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// std::unique_ptr SkCodecImageGenerator::MakeFromEncodedCodec(sk_sp data) { auto codec = SkCodec::MakeFromData(data); @@ -17,16 +50,19 @@ std::unique_ptr SkCodecImageGenerator::MakeFromEncodedCodec(sk return std::unique_ptr(new SkCodecImageGenerator(std::move(codec), data)); } -static SkImageInfo adjust_info(const SkImageInfo& info) { - SkImageInfo newInfo = info; +static SkImageInfo adjust_info(SkCodec* codec) { + SkImageInfo info = codec->getInfo(); if (kUnpremul_SkAlphaType == info.alphaType()) { - newInfo = newInfo.makeAlphaType(kPremul_SkAlphaType); + info = info.makeAlphaType(kPremul_SkAlphaType); } - return newInfo; + if (should_swap_width_height(codec->getOrigin())) { + info = swap_width_height(info); + } + return info; } SkCodecImageGenerator::SkCodecImageGenerator(std::unique_ptr codec, sk_sp data) - : INHERITED(adjust_info(codec->getInfo())) + : INHERITED(adjust_info(codec.get())) , fCodec(std::move(codec)) , fData(std::move(data)) {} @@ -35,13 +71,35 @@ SkData* SkCodecImageGenerator::onRefEncodedData() { return SkRef(fData.get()); } -bool SkCodecImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, - const Options& opts) { +bool SkCodecImageGenerator::onGetPixels(const SkImageInfo& requestInfo, void* requestPixels, + size_t requestRowBytes, const Options& opts) { + const SkCodec::Origin origin = fCodec->getOrigin(); + const SkPixmap request(requestInfo, requestPixels, requestRowBytes); + const SkPixmap* codecMap = &request; + SkAutoPixmapStorage storage; // used if we have to post-orient the output from the codec + + if (origin != SkCodec::kTopLeft_Origin) { + SkImageInfo info = requestInfo; + if (should_swap_width_height(origin)) { + info = swap_width_height(info); + } + // need a tmp buffer to receive the pixels, so we can post-orient them + if (!storage.tryAlloc(info)) { + return false; + } + codecMap = &storage; + } + SkCodec::Options codecOpts; codecOpts.fPremulBehavior = opts.fBehavior; - SkCodec::Result result = fCodec->getPixels(info, pixels, rowBytes, &codecOpts); + SkCodec::Result result = fCodec->getPixels(*codecMap, &codecOpts); switch (result) { case SkCodec::kSuccess: + if (codecMap != &request) { + return SkPixmapPriv::Orient(request, *codecMap, + SkPixmapPriv::OriginToOrient(origin)); + } + // fall through case SkCodec::kIncompleteInput: case SkCodec::kErrorInInput: return true; diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp index 1cee0a9d02..7ce97d23f2 100644 --- a/src/core/SkPixmap.cpp +++ b/src/core/SkPixmap.cpp @@ -15,9 +15,10 @@ #include "SkMask.h" #include "SkNx.h" #include "SkPM4f.h" -#include "SkPixmap.h" +#include "SkPixmapPriv.h" #include "SkReadPixelsRec.h" #include "SkSurface.h" +#include "SkTemplates.h" #include "SkUtils.h" ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -381,3 +382,82 @@ bool SkPixmap::computeIsOpaque() const { } return false; } + +////////////////////////////////////////////////////////////////////////////////////////////////// + +template +void apply(const SkPixmap& dst, const SkPixmap& src, unsigned flags, F getAddr) { + const int maxX = dst.width() - 1; + const int maxY = dst.height() - 1; + T* dstRow = (T*)dst.writable_addr(); + for (int dy = 0; dy < dst.height(); ++dy) { + for (int dx = 0; dx < dst.width(); ++dx) { + int sx = dx; + int sy = dy; + if (flags & SkPixmapPriv::kMirrorX) { + sx = maxX - sx; + } + if (flags & SkPixmapPriv::kMirrorY) { + sy = maxY - sy; + } + if (flags & SkPixmapPriv::kSwapXY) { + SkTSwap(sx, sy); + } + dstRow[dx] = getAddr(src, sx, sy); + } + dstRow = SkTAddOffset(dstRow, dst.rowBytes()); + } +} + +static bool apply_orientation(const SkPixmap& dst, const SkPixmap& src, unsigned flags) { + SkASSERT(dst.colorType() == src.colorType()); + + switch (dst.info().bytesPerPixel()) { + case 1: + apply(dst, src, flags, [](const SkPixmap& pm, int x, int y) { + return *pm.addr8(x, y); + }); break; + case 2: + apply(dst, src, flags, [](const SkPixmap& pm, int x, int y) { + return *pm.addr16(x, y); + }); break; + case 4: + apply(dst, src, flags, [](const SkPixmap& pm, int x, int y) { + return *pm.addr32(x, y); + }); break; + case 8: + apply(dst, src, flags, [](const SkPixmap& pm, int x, int y) { + return *pm.addr64(x, y); + }); break; + default: + return false; + } + return true; +} + +bool SkPixmapPriv::Orient(const SkPixmap& dst, const SkPixmap& src, OrientFlags flags) { + SkASSERT((flags & ~(kMirrorX | kMirrorY | kSwapXY)) == 0); + if (src.colorType() != dst.colorType()) { + return false; + } + // note: we just ignore alphaType and colorSpace for this transformation + + int w = src.width(); + int h = src.height(); + if (flags & kSwapXY) { + SkTSwap(w, h); + } + if (dst.width() != w || dst.height() != h) { + return false; + } + if (w == 0 || h == 0) { + return true; + } + + // check for aliasing to self + if (src.addr() == dst.addr()) { + return flags == 0; + } + return apply_orientation(dst, src, flags); +} + diff --git a/src/core/SkPixmapPriv.h b/src/core/SkPixmapPriv.h new file mode 100644 index 0000000000..dfc9ce2dd0 --- /dev/null +++ b/src/core/SkPixmapPriv.h @@ -0,0 +1,33 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPixmapPriv_DEFINED +#define SkPixmapPriv_DEFINED + +#include "SkPixmap.h" +#include "SkCodec.h" + +class SkPixmapPriv { +public: + // These flag are applied in this order (swap is applied last) + enum OrientFlags { + kMirrorX = 1 << 0, + kMirrorY = 1 << 1, + kSwapXY = 1 << 2, + }; + + static OrientFlags OriginToOrient(SkCodec::Origin); + + /** + * Copy the pixels in this pixmap into dst, applying the orientation transformations specified + * by the flags. If the inputs are invalid, this returns false and no copy is made. + */ + static bool Orient(const SkPixmap& dst, const SkPixmap& src, OrientFlags); +}; + +#endif +