Index: src/core/SkMipMap.cpp |
diff --git a/src/core/SkMipMap.cpp b/src/core/SkMipMap.cpp |
index 1b630a0466cede5d910049e262a298a130bdc0cc..d766dfa8a56378debe58255589087ca7790423fa 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; |