aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar reed <reed@google.com>2016-01-15 13:17:08 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2016-01-15 13:17:08 -0800
commit32e0b4a34a2d461927056677e0ef99241e29df0d (patch)
treee5734690c158529a79a74b01e34cc01ec80c5192 /src
parentc33065a93ad0874672c4c66b9711aa0b3ef7b7e7 (diff)
use triangle filter for odd dimensions in mip-levels
Diffstat (limited to 'src')
-rw-r--r--src/core/SkMipMap.cpp303
1 files changed, 302 insertions, 1 deletions
diff --git a/src/core/SkMipMap.cpp b/src/core/SkMipMap.cpp
index 6e0aaed697..6e921c7942 100644
--- a/src/core/SkMipMap.cpp
+++ b/src/core/SkMipMap.cpp
@@ -9,6 +9,8 @@
#include "SkBitmap.h"
#include "SkColorPriv.h"
+#ifdef SK_SUPPORT_LEGACY_MIPLEVEL_BUILDER
+
static void downsample32_nocheck(void* dst, int, int, const void* srcPtr, const SkPixmap& srcPM) {
const uint32_t* p = static_cast<const uint32_t*>(srcPtr);
const uint32_t* baseP = p;
@@ -132,7 +134,7 @@ static void downsample4444(void* dst, int x, int y, const void* srcPtr, const Sk
}
c += expand4444(*p);
- *((uint16_t*)dst) = (uint16_t)collaps4444(c >> 2);
+ *((uint16_t*)dst) = (uint16_t)collaps4444(c >> 2);
}
static void downsample8_nocheck(void* dst, int, int, const void* srcPtr, const SkPixmap& srcPM) {
@@ -314,9 +316,308 @@ SkMipMap* SkMipMap::Build(const SkBitmap& src, SkDiscardableFactoryProc fact) {
addr += height * rowBytes;
}
SkASSERT(addr == baseAddr + size);
+
+ return mipmap;
+}
+
+#else // new technique that handles odd dimensions better
+
+//
+// ColorTypeFilter is the "Type" we pass to some downsample template functions.
+// It controls how we expand a pixel into a large type, with space between each component,
+// so we can then perform our simple filter (either box or triangle) and store the intermediates
+// in the expanded type.
+//
+
+struct ColorTypeFilter_8888 {
+ typedef uint32_t Type;
+ static uint64_t Expand(uint32_t x) {
+ return (x & 0xFF00FF) | ((uint64_t)(x & 0xFF00FF00) << 24);
+ }
+ static uint32_t Compact(uint64_t x) {
+ return (uint32_t)((x & 0xFF00FF) | ((x >> 24) & 0xFF00FF00));
+ }
+};
+
+struct ColorTypeFilter_565 {
+ typedef uint16_t Type;
+ static uint32_t Expand(uint16_t x) {
+ return (x & ~SK_G16_MASK_IN_PLACE) | ((x & SK_G16_MASK_IN_PLACE) << 16);
+ }
+ static uint16_t Compact(uint32_t x) {
+ return (x & ~SK_G16_MASK_IN_PLACE) | ((x >> 16) & SK_G16_MASK_IN_PLACE);
+ }
+};
+
+struct ColorTypeFilter_4444 {
+ typedef uint16_t Type;
+ static uint32_t Expand(uint16_t x) {
+ return (x & 0xF0F) | ((x & ~0xF0F) << 12);
+ }
+ static uint16_t Compact(uint32_t x) {
+ return (x & 0xF0F) | ((x >> 12) & ~0xF0F);
+ }
+};
+
+struct ColorTypeFilter_8 {
+ typedef uint8_t Type;
+ static unsigned Expand(unsigned x) {
+ return x;
+ }
+ static uint8_t Compact(unsigned x) {
+ return (uint8_t)x;
+ }
+};
+
+template <typename T> T add_121(T a, T b, T c) {
+ return a + b + b + c;
+}
+
+//
+// To produce each mip level, we need to filter down by 1/2 (e.g. 100x100 -> 50,50)
+// If the starting dimension is odd, we floor the size of the lower level (e.g. 101 -> 50)
+// 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.
+//
+
+template <typename F> void downsample_2_2(void* dst, const void* src, size_t srcRB) {
+ auto p0 = static_cast<const typename F::Type*>(src);
+ auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
+
+ 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;
+ *(typename F::Type*)dst = F::Compact(c >> 2);
+}
+
+template <typename F> void downsample_3_2(void* dst, const void* src, size_t srcRB) {
+ auto p0 = static_cast<const typename F::Type*>(src);
+ auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
+
+ auto c00 = F::Expand(p0[0]);
+ auto c01 = F::Expand(p0[1]);
+ auto c02 = F::Expand(p0[2]);
+ auto c10 = F::Expand(p1[0]);
+ auto c11 = F::Expand(p1[1]);
+ auto c12 = F::Expand(p1[2]);
+
+ auto c = add_121(c00, c01, c02) + add_121(c10, c11, c12);
+ *(typename F::Type*)dst = F::Compact(c >> 3);
+}
+
+template <typename F> void downsample_2_3(void* dst, const void* src, size_t srcRB) {
+ 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 c00 = F::Expand(p0[0]);
+ auto c01 = F::Expand(p0[1]);
+ auto c10 = F::Expand(p1[0]);
+ auto c11 = F::Expand(p1[1]);
+ auto c20 = F::Expand(p2[0]);
+ auto c21 = F::Expand(p2[1]);
+ auto c = add_121(c00, c10, c20) + add_121(c01, c11, c21);
+ *(typename F::Type*)dst = F::Compact(c >> 3);
+}
+
+template <typename F> void downsample_3_3(void* dst, const void* src, size_t srcRB) {
+ 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 c00 = F::Expand(p0[0]);
+ auto c01 = F::Expand(p0[1]);
+ auto c02 = F::Expand(p0[2]);
+ auto c10 = F::Expand(p1[0]);
+ auto c11 = F::Expand(p1[1]);
+ auto c12 = F::Expand(p1[2]);
+ auto c20 = F::Expand(p2[0]);
+ auto c21 = F::Expand(p2[1]);
+ auto c22 = F::Expand(p2[2]);
+
+ auto c = add_121(c00, c01, c02) + (add_121(c10, c11, c12) << 1) + add_121(c20, c21, c22);
+ *(typename F::Type*)dst = F::Compact(c >> 4);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+size_t SkMipMap::AllocLevelsSize(int levelCount, size_t pixelSize) {
+ if (levelCount < 0) {
+ return 0;
+ }
+ int64_t size = sk_64_mul(levelCount + 1, sizeof(Level)) + pixelSize;
+ if (!sk_64_isS32(size)) {
+ return 0;
+ }
+ return sk_64_asS32(size);
+}
+
+SkMipMap* SkMipMap::Build(const SkBitmap& src, SkDiscardableFactoryProc fact) {
+ typedef void FilterProc(void*, const void* srcPtr, size_t srcRB);
+
+ FilterProc* proc_2_2 = nullptr;
+ FilterProc* proc_2_3 = nullptr;
+ FilterProc* proc_3_2 = nullptr;
+ FilterProc* proc_3_3 = nullptr;
+
+ const SkColorType ct = src.colorType();
+ const SkAlphaType at = src.alphaType();
+ switch (ct) {
+ case kRGBA_8888_SkColorType:
+ case kBGRA_8888_SkColorType:
+ proc_2_2 = downsample_2_2<ColorTypeFilter_8888>;
+ proc_2_3 = downsample_2_3<ColorTypeFilter_8888>;
+ proc_3_2 = downsample_3_2<ColorTypeFilter_8888>;
+ proc_3_3 = downsample_3_3<ColorTypeFilter_8888>;
+ break;
+ case kRGB_565_SkColorType:
+ proc_2_2 = downsample_2_2<ColorTypeFilter_565>;
+ proc_2_3 = downsample_2_3<ColorTypeFilter_565>;
+ proc_3_2 = downsample_3_2<ColorTypeFilter_565>;
+ proc_3_3 = downsample_3_3<ColorTypeFilter_565>;
+ break;
+ case kARGB_4444_SkColorType:
+ proc_2_2 = downsample_2_2<ColorTypeFilter_4444>;
+ proc_2_3 = downsample_2_3<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_2_2 = downsample_2_2<ColorTypeFilter_8>;
+ proc_2_3 = downsample_2_3<ColorTypeFilter_8>;
+ proc_3_2 = downsample_3_2<ColorTypeFilter_8>;
+ proc_3_3 = downsample_3_3<ColorTypeFilter_8>;
+ break;
+ default:
+ // TODO: We could build miplevels for kIndex8 if the levels were in 8888.
+ // Means using more ram, but the quality would be fine.
+ return nullptr;
+ }
+
+ // whip through our loop to compute the exact size needed
+ size_t size = 0;
+ int countLevels = 0;
+ {
+ int width = src.width();
+ int height = src.height();
+ for (;;) {
+ width >>= 1;
+ height >>= 1;
+ if (0 == width || 0 == height) {
+ break;
+ }
+ size += SkColorTypeMinRowBytes(ct, width) * height;
+ countLevels += 1;
+ }
+ }
+ if (0 == countLevels) {
+ return nullptr;
+ }
+
+ size_t storageSize = SkMipMap::AllocLevelsSize(countLevels, size);
+ if (0 == storageSize) {
+ return nullptr;
+ }
+
+ SkAutoPixmapUnlock srcUnlocker;
+ if (!src.requestLock(&srcUnlocker)) {
+ return nullptr;
+ }
+ const SkPixmap& srcPixmap = srcUnlocker.pixmap();
+ // Try to catch where we might have returned nullptr for src crbug.com/492818
+ if (nullptr == srcPixmap.addr()) {
+ sk_throw();
+ }
+
+ SkMipMap* mipmap;
+ if (fact) {
+ SkDiscardableMemory* dm = fact(storageSize);
+ if (nullptr == dm) {
+ return nullptr;
+ }
+ mipmap = new SkMipMap(storageSize, dm);
+ } else {
+ mipmap = new SkMipMap(sk_malloc_throw(storageSize), storageSize);
+ }
+
+ // init
+ mipmap->fCount = countLevels;
+ mipmap->fLevels = (Level*)mipmap->writable_data();
+
+ Level* levels = mipmap->fLevels;
+ uint8_t* baseAddr = (uint8_t*)&levels[countLevels];
+ uint8_t* addr = baseAddr;
+ int width = src.width();
+ int height = src.height();
+ uint32_t rowBytes;
+ SkPixmap srcPM(srcPixmap);
+
+ int prevW = width;
+ int prevH = height;
+ for (int i = 0; i < countLevels; ++i) {
+ width >>= 1;
+ height >>= 1;
+ rowBytes = SkToU32(SkColorTypeMinRowBytes(ct, width));
+
+ levels[i].fPixels = addr;
+ levels[i].fWidth = width;
+ levels[i].fHeight = height;
+ levels[i].fRowBytes = rowBytes;
+ levels[i].fScale = (float)width / src.width();
+
+ SkPixmap dstPM(SkImageInfo::Make(width, height, ct, at), addr, rowBytes);
+
+ const size_t pixelSize = srcPM.info().bytesPerPixel();
+
+ const void* srcBasePtr = srcPM.addr();
+ void* dstBasePtr = dstPM.writable_addr();
+
+ FilterProc* proc;
+ if (prevH & 1) { // src-height is 3
+ if (prevW & 1) { // src-width is 3
+ proc = proc_3_3;
+ } else { // src-width is 2
+ proc = proc_2_3;
+ }
+ } else { // src-height is 2
+ if (prevW & 1) { // src-width is 3
+ proc = proc_3_2;
+ } else { // src-width is 2
+ proc = proc_2_2;
+ }
+ }
+
+ const size_t srcRB = srcPM.rowBytes();
+ for (int y = 0; y < height; y++) {
+ const void* srcPtr = srcBasePtr;
+ void* dstPtr = dstBasePtr;
+
+ for (int x = 0; x < width; x++) {
+ proc(dstPtr, srcPtr, srcRB);
+ srcPtr = (char*)srcPtr + pixelSize * 2;
+ dstPtr = (char*)dstPtr + pixelSize;
+ }
+
+ srcBasePtr = (char*)srcBasePtr + srcRB * 2; // jump two rows
+ dstBasePtr = (char*)dstBasePtr + dstPM.rowBytes();
+ }
+ srcPM = dstPM;
+ addr += height * rowBytes;
+ prevW = width;
+ prevH = height;
+ }
+ SkASSERT(addr == baseAddr + size);
return mipmap;
}
+#endif
///////////////////////////////////////////////////////////////////////////////