aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkMipMap.cpp
diff options
context:
space:
mode:
authorGravatar cblume <cblume@chromium.org>2016-03-01 13:54:30 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2016-03-01 13:54:30 -0800
commit5b9ad7620b36858f99fef0763d7fc04d024fd71d (patch)
treec367762db0ca87a2234a3fedfbdb1b9392747fda /src/core/SkMipMap.cpp
parent7dd5db43a2eb0f43437ebc37f5c01b481cd53839 (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.cpp201
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;