diff options
author | cblume <cblume@chromium.org> | 2016-03-01 13:54:30 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-01 13:54:30 -0800 |
commit | 5b9ad7620b36858f99fef0763d7fc04d024fd71d (patch) | |
tree | c367762db0ca87a2234a3fedfbdb1b9392747fda /src/core/SkMipMap.cpp | |
parent | 7dd5db43a2eb0f43437ebc37f5c01b481cd53839 (diff) |
Adding anisotropic mipmap levels to SkMipMap.
Adding 1x2, 1x3, 2x1, 3x1 filters to SkMipMap and enabling SkMipMap to generate anisotropic mip levels.
BUG=590804
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1750303002
Review URL: https://codereview.chromium.org/1750303002
Diffstat (limited to 'src/core/SkMipMap.cpp')
-rw-r--r-- | src/core/SkMipMap.cpp | 201 |
1 files changed, 153 insertions, 48 deletions
diff --git a/src/core/SkMipMap.cpp b/src/core/SkMipMap.cpp index 1b630a0466..d766dfa8a5 100644 --- a/src/core/SkMipMap.cpp +++ b/src/core/SkMipMap.cpp @@ -80,52 +80,86 @@ template <typename T> T add_121(const T& a, const T& b, const T& c) { // In those (odd) cases, we use a triangle filter, with 1-pixel overlap between samplings, // else for even cases, we just use a 2x box filter. // -// This produces 4 possible filters: 2x2 2x3 3x2 3x3 where WxH indicates the number of src pixels -// we need to sample in each dimension to produce 1 dst pixel. +// This produces 4 possible isotropic filters: 2x2 2x3 3x2 3x3 where WxH indicates the number of +// src pixels we need to sample in each dimension to produce 1 dst pixel. // +// OpenGL expects a full mipmap stack to contain anisotropic space as well. +// This means a 100x1 image would continue down to a 50x1 image, 25x1 image... +// Because of this, we need 4 more anisotropic filters: 1x2, 1x3, 2x1, 3x1. -template <typename F> void downsample_2_2(void* dst, const void* src, size_t srcRB, int count) { +template <typename F> void downsample_1_2(void* dst, const void* src, size_t srcRB, int count) { + SkASSERT(count > 0); auto p0 = static_cast<const typename F::Type*>(src); auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); auto d = static_cast<typename F::Type*>(dst); for (int i = 0; i < count; ++i) { auto c00 = F::Expand(p0[0]); - auto c01 = F::Expand(p0[1]); auto c10 = F::Expand(p1[0]); - auto c11 = F::Expand(p1[1]); - auto c = c00 + c10 + c01 + c11; + auto c = c00 + c10; + d[i] = F::Compact(c >> 1); + p0 += 2; + p1 += 2; + } +} + +template <typename F> void downsample_1_3(void* dst, const void* src, size_t srcRB, int count) { + SkASSERT(count > 0); + auto p0 = static_cast<const typename F::Type*>(src); + auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); + auto p2 = (const typename F::Type*)((const char*)p1 + srcRB); + auto d = static_cast<typename F::Type*>(dst); + + for (int i = 0; i < count; ++i) { + auto c00 = F::Expand(p0[0]); + auto c10 = F::Expand(p1[0]); + auto c20 = F::Expand(p2[0]); + + auto c = add_121(c00, c10, c20); d[i] = F::Compact(c >> 2); p0 += 2; p1 += 2; + p2 += 2; } } -template <typename F> void downsample_3_2(void* dst, const void* src, size_t srcRB, int count) { +template <typename F> void downsample_2_1(void* dst, const void* src, size_t srcRB, int count) { + SkASSERT(count > 0); + auto p0 = static_cast<const typename F::Type*>(src); + auto d = static_cast<typename F::Type*>(dst); + + for (int i = 0; i < count; ++i) { + auto c00 = F::Expand(p0[0]); + auto c01 = F::Expand(p0[1]); + + auto c = c00 + c01; + d[i] = F::Compact(c >> 1); + p0 += 2; + } +} + +template <typename F> void downsample_2_2(void* dst, const void* src, size_t srcRB, int count) { SkASSERT(count > 0); auto p0 = static_cast<const typename F::Type*>(src); auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); auto d = static_cast<typename F::Type*>(dst); - auto c02 = F::Expand(p0[0]); - auto c12 = F::Expand(p1[0]); for (int i = 0; i < count; ++i) { - auto c00 = c02; + auto c00 = F::Expand(p0[0]); auto c01 = F::Expand(p0[1]); - c02 = F::Expand(p0[2]); - auto c10 = c12; + auto c10 = F::Expand(p1[0]); auto c11 = F::Expand(p1[1]); - c12 = F::Expand(p1[2]); - auto c = add_121(c00, c01, c02) + add_121(c10, c11, c12); - d[i] = F::Compact(c >> 3); + auto c = c00 + c10 + c01 + c11; + d[i] = F::Compact(c >> 2); p0 += 2; p1 += 2; } } template <typename F> void downsample_2_3(void* dst, const void* src, size_t srcRB, int count) { + SkASSERT(count > 0); auto p0 = static_cast<const typename F::Type*>(src); auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); auto p2 = (const typename F::Type*)((const char*)p1 + srcRB); @@ -147,7 +181,48 @@ template <typename F> void downsample_2_3(void* dst, const void* src, size_t src } } +template <typename F> void downsample_3_1(void* dst, const void* src, size_t srcRB, int count) { + SkASSERT(count > 0); + auto p0 = static_cast<const typename F::Type*>(src); + auto d = static_cast<typename F::Type*>(dst); + + auto c02 = F::Expand(p0[0]); + for (int i = 0; i < count; ++i) { + auto c00 = c02; + auto c01 = F::Expand(p0[1]); + c02 = F::Expand(p0[2]); + + auto c = add_121(c00, c01, c02); + d[i] = F::Compact(c >> 2); + p0 += 2; + } +} + +template <typename F> void downsample_3_2(void* dst, const void* src, size_t srcRB, int count) { + SkASSERT(count > 0); + auto p0 = static_cast<const typename F::Type*>(src); + auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); + auto d = static_cast<typename F::Type*>(dst); + + auto c02 = F::Expand(p0[0]); + auto c12 = F::Expand(p1[0]); + for (int i = 0; i < count; ++i) { + auto c00 = c02; + auto c01 = F::Expand(p0[1]); + c02 = F::Expand(p0[2]); + auto c10 = c12; + auto c11 = F::Expand(p1[1]); + c12 = F::Expand(p1[2]); + + auto c = add_121(c00, c01, c02) + add_121(c10, c11, c12); + d[i] = F::Compact(c >> 3); + p0 += 2; + p1 += 2; + } +} + template <typename F> void downsample_3_3(void* dst, const void* src, size_t srcRB, int count) { + SkASSERT(count > 0); auto p0 = static_cast<const typename F::Type*>(src); auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); auto p2 = (const typename F::Type*)((const char*)p1 + srcRB); @@ -191,8 +266,12 @@ size_t SkMipMap::AllocLevelsSize(int levelCount, size_t pixelSize) { SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) { typedef void FilterProc(void*, const void* srcPtr, size_t srcRB, int count); + FilterProc* proc_1_2 = nullptr; + FilterProc* proc_1_3 = nullptr; + FilterProc* proc_2_1 = nullptr; FilterProc* proc_2_2 = nullptr; FilterProc* proc_2_3 = nullptr; + FilterProc* proc_3_1 = nullptr; FilterProc* proc_3_2 = nullptr; FilterProc* proc_3_3 = nullptr; @@ -201,27 +280,43 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) { switch (ct) { case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: + proc_1_2 = downsample_1_2<ColorTypeFilter_8888>; + proc_1_3 = downsample_1_3<ColorTypeFilter_8888>; + proc_2_1 = downsample_2_1<ColorTypeFilter_8888>; proc_2_2 = downsample_2_2<ColorTypeFilter_8888>; proc_2_3 = downsample_2_3<ColorTypeFilter_8888>; + proc_3_1 = downsample_3_1<ColorTypeFilter_8888>; proc_3_2 = downsample_3_2<ColorTypeFilter_8888>; proc_3_3 = downsample_3_3<ColorTypeFilter_8888>; break; case kRGB_565_SkColorType: + proc_1_2 = downsample_1_2<ColorTypeFilter_565>; + proc_1_3 = downsample_1_3<ColorTypeFilter_565>; + proc_2_1 = downsample_2_1<ColorTypeFilter_565>; proc_2_2 = downsample_2_2<ColorTypeFilter_565>; proc_2_3 = downsample_2_3<ColorTypeFilter_565>; + proc_3_1 = downsample_3_1<ColorTypeFilter_565>; proc_3_2 = downsample_3_2<ColorTypeFilter_565>; proc_3_3 = downsample_3_3<ColorTypeFilter_565>; break; case kARGB_4444_SkColorType: + proc_1_2 = downsample_1_2<ColorTypeFilter_4444>; + proc_1_3 = downsample_1_3<ColorTypeFilter_4444>; + proc_2_1 = downsample_2_1<ColorTypeFilter_4444>; proc_2_2 = downsample_2_2<ColorTypeFilter_4444>; proc_2_3 = downsample_2_3<ColorTypeFilter_4444>; + proc_3_1 = downsample_3_1<ColorTypeFilter_4444>; proc_3_2 = downsample_3_2<ColorTypeFilter_4444>; proc_3_3 = downsample_3_3<ColorTypeFilter_4444>; break; case kAlpha_8_SkColorType: case kGray_8_SkColorType: + proc_1_2 = downsample_1_2<ColorTypeFilter_8>; + proc_1_3 = downsample_1_3<ColorTypeFilter_8>; + proc_2_1 = downsample_2_1<ColorTypeFilter_8>; proc_2_2 = downsample_2_2<ColorTypeFilter_8>; proc_2_3 = downsample_2_3<ColorTypeFilter_8>; + proc_3_1 = downsample_3_1<ColorTypeFilter_8>; proc_3_2 = downsample_3_2<ColorTypeFilter_8>; proc_3_3 = downsample_3_3<ColorTypeFilter_8>; break; @@ -231,6 +326,9 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) { return nullptr; } + if (src.width() <= 1 && src.height() <= 1) { + return nullptr; + } // whip through our loop to compute the exact size needed size_t size = 0; int countLevels = 0; @@ -238,18 +336,15 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) { int width = src.width(); int height = src.height(); for (;;) { - width >>= 1; - height >>= 1; - if (0 == width || 0 == height) { - break; - } + width = SkTMax(1, width >> 1); + height = SkTMax(1, height >> 1); size += SkColorTypeMinRowBytes(ct, width) * height; countLevels += 1; + if (1 == width && 1 == height) { + break; + } } } - if (0 == countLevels) { - return nullptr; - } SkASSERT(countLevels == SkMipMap::ComputeLevelCount(src.width(), src.height())); @@ -283,21 +378,37 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) { for (int i = 0; i < countLevels; ++i) { FilterProc* proc; - if (height & 1) { // src-height is 3 - if (width & 1) { // src-width is 3 - proc = proc_3_3; - } else { // src-width is 2 - proc = proc_2_3; + if (height & 1) { + if (height == 1) { // src-height is 1 + if (width & 1) { // src-width is 3 + proc = proc_3_1; + } else { // src-width is 2 + proc = proc_2_1; + } + } else { // src-height is 3 + if (width & 1) { + if (width == 1) { // src-width is 1 + proc = proc_1_3; + } else { // src-width is 3 + proc = proc_3_3; + } + } else { // src-width is 2 + proc = proc_2_3; + } } - } else { // src-height is 2 - if (width & 1) { // src-width is 3 - proc = proc_3_2; - } else { // src-width is 2 + } else { // src-height is 2 + if (width & 1) { + if (width == 1) { // src-width is 1 + proc = proc_1_2; + } else { // src-width is 3 + proc = proc_3_2; + } + } else { // src-width is 2 proc = proc_2_2; } } - width >>= 1; - height >>= 1; + width = SkTMax(1, width >> 1); + height = SkTMax(1, height >> 1); rowBytes = SkToU32(SkColorTypeMinRowBytes(ct, width)); levels[i].fPixmap = SkPixmap(SkImageInfo::Make(width, height, ct, at), addr, rowBytes); @@ -323,27 +434,21 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) { } int SkMipMap::ComputeLevelCount(int baseWidth, int baseHeight) { + if (baseWidth < 1 || baseHeight < 1) { + return 0; + } + // OpenGL's spec requires that each mipmap level have height/width equal to // max(1, floor(original_height / 2^i) // (or original_width) where i is the mipmap level. // Continue scaling down until both axes are size 1. - // - // This means it maintains isotropic space (both axes scaling down - // at the same rate) until one axis hits size 1. - // At that point, OpenGL continues to scale down into anisotropic space - // (where the scales are not the same between axes). - // - // Skia currently does not go into anisotropic space. - // Once an axis hits size 1 we stop. - // All this means is rather than use the largest axis we will use the - // smallest axis. - - const int smallestAxis = SkTMin(baseWidth, baseHeight); - if (smallestAxis < 2) { + + const int largestAxis = SkTMax(baseWidth, baseHeight); + if (largestAxis < 2) { // SkMipMap::Build requires a minimum size of 2. return 0; } - const int leadingZeros = SkCLZ(static_cast<uint32_t>(smallestAxis)); + const int leadingZeros = SkCLZ(static_cast<uint32_t>(largestAxis)); // If the value 00011010 has 3 leading 0s then it has 5 significant bits // (the bits which are not leading zeros) const int significantBits = (sizeof(uint32_t) * 8) - leadingZeros; |