}
// 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 = SkTMax(1, width >> 1);
- height = SkTMax(1, height >> 1);
- size += SkColorTypeMinRowBytes(ct, width) * height;
- countLevels += 1;
- if (1 == width && 1 == height) {
- break;
- }
- }
+ int countLevels = ComputeLevelCount(src.width(), src.height());
+ for (int currentMipLevel = countLevels; currentMipLevel > 0; currentMipLevel--) {
+ SkSize mipSize = ComputeLevelSize(src.width(), src.height(), currentMipLevel);
+ size += SkColorTypeMinRowBytes(ct, mipSize.fWidth) * mipSize.fHeight;
}
- SkASSERT(countLevels == SkMipMap::ComputeLevelCount(src.width(), src.height()));
-
size_t storageSize = SkMipMap::AllocLevelsSize(countLevels, size);
if (0 == storageSize) {
return nullptr;
return mipLevelCount;
}
+SkSize SkMipMap::ComputeLevelSize(int baseWidth, int baseHeight, int level) {
+ if (baseWidth < 1 || baseHeight < 1) {
+ return SkSize::Make(0, 0);
+ }
+
+ int maxLevelCount = ComputeLevelCount(baseWidth, baseHeight);
+ if (level > maxLevelCount || level < 0) {
+ return SkSize::Make(0, 0);
+ }
+ if (level == 0) {
+ return SkSize::Make(baseWidth, baseHeight);
+ }
+
+ // 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.
+
+ int width = SkTMax(1, baseWidth >> level);
+ int height = SkTMax(1, baseHeight >> level);
+
+ return SkSize::Make(width, height);
+}
+
///////////////////////////////////////////////////////////////////////////////
bool SkMipMap::extractLevel(const SkSize& scaleSize, Level* levelPtr) const {
REPORTER_ASSERT(reporter, currentTest.fExpectedLevelCount == levelCount);
}
}
+
+struct LevelSizeScenario {
+ int fBaseWidth;
+ int fBaseHeight;
+ int fLevel;
+ SkSize fExpectedMipMapLevelSize;
+};
+
+DEF_TEST(MipMap_ComputeLevelSize, reporter) {
+ const LevelSizeScenario tests[] = {
+ // Test mipmaps with negative sizes
+ {-100, 100, 1, SkSize::Make(0, 0)},
+ {100, -100, 1, SkSize::Make(0, 0)},
+ {-100, -100, 1, SkSize::Make(0, 0)},
+
+ // Test mipmaps with 0, 1, 2 as dimensions
+ // (SkMipMap::Build requires a min size of 1)
+ //
+ // 0
+ {0, 100, 1, SkSize::Make(0, 0)},
+ {100, 0, 1, SkSize::Make(0, 0)},
+ {0, 0, 1, SkSize::Make(0, 0)},
+ // 1
+
+ {1, 100, 1, SkSize::Make(1, 50)},
+ {100, 1, 1, SkSize::Make(50, 1)},
+ {1, 1, 1, SkSize::Make(0, 0)},
+ // 2
+ {2, 100, 1, SkSize::Make(1, 50)},
+ {100, 2, 2, SkSize::Make(25, 1)},
+ {2, 2, 1, SkSize::Make(1, 1)},
+
+ // Test a handful of cases
+ {63, 63, 3, SkSize::Make(7, 7)},
+ {64, 64, 3, SkSize::Make(8, 8)},
+ {127, 127, 3, SkSize::Make(15, 15)},
+ {64, 129, 4, SkSize::Make(4, 8)},
+ {255, 32, 7, SkSize::Make(1, 1)},
+ {500, 1000, 2, SkSize::Make(125, 250)},
+ };
+
+ for (auto& currentTest : tests) {
+ SkSize levelSize = SkMipMap::ComputeLevelSize(currentTest.fBaseWidth,
+ currentTest.fBaseHeight, currentTest.fLevel);
+ REPORTER_ASSERT(reporter, currentTest.fExpectedMipMapLevelSize == levelSize);
+ }
+}