Adding direct access to SkMipMap levels.
BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1639693002 Review URL: https://codereview.chromium.org/1639693002
This commit is contained in:
parent
3bc2624a4b
commit
e2412d5738
@ -8,7 +8,9 @@
|
||||
#include "SkMipMap.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkMath.h"
|
||||
#include "SkNx.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
//
|
||||
// ColorTypeFilter is the "Type" we pass to some downsample template functions.
|
||||
@ -249,6 +251,8 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkASSERT(countLevels == SkMipMap::ComputeLevelCount(src.width(), src.height()));
|
||||
|
||||
size_t storageSize = SkMipMap::AllocLevelsSize(countLevels, size);
|
||||
if (0 == storageSize) {
|
||||
return nullptr;
|
||||
@ -318,6 +322,46 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
|
||||
return mipmap;
|
||||
}
|
||||
|
||||
int SkMipMap::ComputeLevelCount(int baseWidth, int 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.
|
||||
// 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) {
|
||||
// SkMipMap::Build requires a minimum size of 2.
|
||||
return 0;
|
||||
}
|
||||
const int leadingZeros = SkCLZ(static_cast<uint32_t>(smallestAxis));
|
||||
// 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;
|
||||
// This is making the assumption that the size of a byte is 8 bits
|
||||
// and that sizeof(uint32_t)'s implementation-defined behavior is 4.
|
||||
int mipLevelCount = significantBits;
|
||||
|
||||
// SkMipMap does not include the base mip level.
|
||||
// For example, it contains levels 1-x instead of 0-x.
|
||||
// This is because the image used to create SkMipMap is the base level.
|
||||
// So subtract 1 from the mip level count.
|
||||
if (mipLevelCount > 0) {
|
||||
--mipLevelCount;
|
||||
}
|
||||
|
||||
return mipLevelCount;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SkMipMap::extractLevel(const SkSize& scaleSize, Level* levelPtr) const {
|
||||
@ -379,3 +423,22 @@ SkMipMap* SkMipMap::Build(const SkBitmap& src, SkDiscardableFactoryProc fact) {
|
||||
return Build(srcPixmap, fact);
|
||||
}
|
||||
|
||||
int SkMipMap::countLevels() const {
|
||||
return fCount;
|
||||
}
|
||||
|
||||
bool SkMipMap::getLevel(int index, Level* levelPtr) const {
|
||||
if (NULL == fLevels) {
|
||||
return false;
|
||||
}
|
||||
if (index < 0) {
|
||||
return false;
|
||||
}
|
||||
if (index > fCount - 1) {
|
||||
return false;
|
||||
}
|
||||
if (levelPtr) {
|
||||
*levelPtr = fLevels[index];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -23,12 +23,18 @@ public:
|
||||
static SkMipMap* Build(const SkPixmap& src, SkDiscardableFactoryProc);
|
||||
static SkMipMap* Build(const SkBitmap& src, SkDiscardableFactoryProc);
|
||||
|
||||
// This function lets you determine how many levels a SkMipMap will have without
|
||||
// creating that mipmap.
|
||||
static int ComputeLevelCount(int baseWidth, int baseHeight);
|
||||
|
||||
struct Level {
|
||||
SkPixmap fPixmap;
|
||||
SkSize fScale; // < 1.0
|
||||
};
|
||||
|
||||
bool extractLevel(const SkSize& scale, Level*) const;
|
||||
int countLevels() const;
|
||||
bool getLevel(int index, Level*) const;
|
||||
|
||||
protected:
|
||||
void onDataChange(void* oldData, void* newData) override {
|
||||
|
@ -10,12 +10,8 @@
|
||||
#include "SkRandom.h"
|
||||
#include "Test.h"
|
||||
|
||||
static void make_bitmap(SkBitmap* bm, SkRandom& rand) {
|
||||
// for now, Build needs a min size of 2, otherwise it will return nullptr.
|
||||
// should fix that to support 1 X N, where N > 1 to return non-null.
|
||||
int w = 2 + rand.nextU() % 1000;
|
||||
int h = 2 + rand.nextU() % 1000;
|
||||
bm->allocN32Pixels(w, h);
|
||||
static void make_bitmap(SkBitmap* bm, int width, int height) {
|
||||
bm->allocN32Pixels(width, height);
|
||||
bm->eraseColor(SK_ColorWHITE);
|
||||
}
|
||||
|
||||
@ -24,9 +20,14 @@ DEF_TEST(MipMap, reporter) {
|
||||
SkRandom rand;
|
||||
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
make_bitmap(&bm, rand);
|
||||
// for now, Build needs a min size of 2, otherwise it will return nullptr.
|
||||
// should fix that to support 1 X N, where N > 1 to return non-null.
|
||||
int width = 2 + rand.nextU() % 1000;
|
||||
int height = 2 + rand.nextU() % 1000;
|
||||
make_bitmap(&bm, width, height);
|
||||
SkAutoTUnref<SkMipMap> mm(SkMipMap::Build(bm, nullptr));
|
||||
|
||||
REPORTER_ASSERT(reporter, mm->countLevels() == SkMipMap::ComputeLevelCount(width, height));
|
||||
REPORTER_ASSERT(reporter, !mm->extractLevel(SkSize::Make(SK_Scalar1, SK_Scalar1),
|
||||
nullptr));
|
||||
REPORTER_ASSERT(reporter, !mm->extractLevel(SkSize::Make(SK_Scalar1 * 2, SK_Scalar1 * 2),
|
||||
@ -55,3 +56,99 @@ DEF_TEST(MipMap, reporter) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_mipmap_generation(int width, int height, int expectedMipLevelCount,
|
||||
skiatest::Reporter* reporter) {
|
||||
SkBitmap bm;
|
||||
bm.allocN32Pixels(width, height);
|
||||
bm.eraseColor(SK_ColorWHITE);
|
||||
SkAutoTUnref<SkMipMap> mm(SkMipMap::Build(bm, nullptr));
|
||||
|
||||
const int mipLevelCount = mm->countLevels();
|
||||
REPORTER_ASSERT(reporter, mipLevelCount == expectedMipLevelCount);
|
||||
for (int i = 0; i < mipLevelCount; ++i) {
|
||||
SkMipMap::Level level;
|
||||
REPORTER_ASSERT(reporter, mm->getLevel(i, &level));
|
||||
// Make sure the mipmaps contain valid data and that the sizes are correct
|
||||
REPORTER_ASSERT(reporter, level.fPixmap.addr());
|
||||
|
||||
// + 1 because SkMipMap does not include the base mipmap level.
|
||||
int twoToTheMipLevel = 1 << (i + 1);
|
||||
int currentWidth = width / twoToTheMipLevel;
|
||||
int currentHeight = height / twoToTheMipLevel;
|
||||
REPORTER_ASSERT(reporter, level.fPixmap.width() == currentWidth);
|
||||
REPORTER_ASSERT(reporter, level.fPixmap.height() == currentHeight);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(MipMap_DirectLevelAccess, reporter) {
|
||||
// create mipmap with invalid size
|
||||
{
|
||||
// SkMipMap current requires the dimensions be greater than 2x2
|
||||
SkBitmap bm;
|
||||
bm.allocN32Pixels(1, 1);
|
||||
bm.eraseColor(SK_ColorWHITE);
|
||||
SkAutoTUnref<SkMipMap> mm(SkMipMap::Build(bm, nullptr));
|
||||
|
||||
REPORTER_ASSERT(reporter, mm == nullptr);
|
||||
}
|
||||
|
||||
// check small mipmap's count and levels
|
||||
// There should be 5 mipmap levels generated:
|
||||
// 16x16, 8x8, 4x4, 2x2, 1x1
|
||||
test_mipmap_generation(32, 32, 5, reporter);
|
||||
|
||||
// check large mipmap's count and levels
|
||||
// There should be 9 mipmap levels generated:
|
||||
// 500x500, 250x250, 125x125, 62x62, 31x31, 15x15, 7x7, 3x3, 1x1
|
||||
test_mipmap_generation(1000, 1000, 9, reporter);
|
||||
}
|
||||
|
||||
struct LevelCountScenario {
|
||||
int fWidth;
|
||||
int fHeight;
|
||||
int fExpectedLevelCount;
|
||||
};
|
||||
|
||||
DEF_TEST(MipMap_ComputeLevelCount, reporter) {
|
||||
const LevelCountScenario tests[] = {
|
||||
// Test mipmaps with negative sizes
|
||||
{-100, 100, 0},
|
||||
{100, -100, 0},
|
||||
{-100, -100, 0},
|
||||
|
||||
// Test mipmaps with 0, 1, 2 as dimensions
|
||||
// (SkMipMap::Build requires a min size of 2)
|
||||
//
|
||||
// 0
|
||||
{0, 100, 0},
|
||||
{100, 0, 0},
|
||||
{0, 0, 0},
|
||||
// 1
|
||||
{1, 100, 0},
|
||||
{100, 1, 0},
|
||||
{1, 1, 0},
|
||||
// 2
|
||||
{2, 100, 1},
|
||||
{100, 2, 1},
|
||||
{2, 2, 1},
|
||||
|
||||
// Test a handful of boundaries such as 63x63 and 64x64
|
||||
{63, 63, 5},
|
||||
{64, 64, 6},
|
||||
{127, 127, 6},
|
||||
{128, 128, 7},
|
||||
{255, 255, 7},
|
||||
{256, 256, 8},
|
||||
|
||||
// Test different dimensions, such as 256x64
|
||||
{64, 129, 6},
|
||||
{255, 32, 5},
|
||||
{500, 1000, 8}
|
||||
};
|
||||
|
||||
for (auto& currentTest : tests) {
|
||||
int levelCount = SkMipMap::ComputeLevelCount(currentTest.fWidth, currentTest.fHeight);
|
||||
REPORTER_ASSERT(reporter, currentTest.fExpectedLevelCount == levelCount);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user