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
This commit is contained in:
parent
7dd5db43a2
commit
5b9ad7620b
@ -80,11 +80,67 @@ 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,
|
// 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.
|
// 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
|
// This produces 4 possible isotropic filters: 2x2 2x3 3x2 3x3 where WxH indicates the number of
|
||||||
// we need to sample in each dimension to produce 1 dst pixel.
|
// 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_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 c10 = F::Expand(p1[0]);
|
||||||
|
|
||||||
|
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_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) {
|
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 p0 = static_cast<const typename F::Type*>(src);
|
||||||
auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
|
auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
|
||||||
auto d = static_cast<typename F::Type*>(dst);
|
auto d = static_cast<typename F::Type*>(dst);
|
||||||
@ -102,6 +158,46 @@ template <typename F> void downsample_2_2(void* dst, const void* src, size_t src
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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 c20 = F::Expand(p2[0]);
|
||||||
|
auto c21 = F::Expand(p2[1]);
|
||||||
|
|
||||||
|
auto c = add_121(c00, c10, c20) + add_121(c01, c11, c21);
|
||||||
|
d[i] = F::Compact(c >> 3);
|
||||||
|
p0 += 2;
|
||||||
|
p1 += 2;
|
||||||
|
p2 += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
template <typename F> void downsample_3_2(void* dst, const void* src, size_t srcRB, int count) {
|
||||||
SkASSERT(count > 0);
|
SkASSERT(count > 0);
|
||||||
auto p0 = static_cast<const typename F::Type*>(src);
|
auto p0 = static_cast<const typename F::Type*>(src);
|
||||||
@ -125,29 +221,8 @@ template <typename F> void downsample_3_2(void* dst, const void* src, size_t src
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F> void downsample_2_3(void* dst, const void* src, size_t srcRB, int count) {
|
|
||||||
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 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);
|
|
||||||
d[i] = F::Compact(c >> 3);
|
|
||||||
p0 += 2;
|
|
||||||
p1 += 2;
|
|
||||||
p2 += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F> void downsample_3_3(void* dst, const void* src, size_t srcRB, int count) {
|
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 p0 = static_cast<const typename F::Type*>(src);
|
||||||
auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
|
auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
|
||||||
auto p2 = (const typename F::Type*)((const char*)p1 + 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) {
|
SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
|
||||||
typedef void FilterProc(void*, const void* srcPtr, size_t srcRB, int count);
|
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_2 = nullptr;
|
||||||
FilterProc* proc_2_3 = nullptr;
|
FilterProc* proc_2_3 = nullptr;
|
||||||
|
FilterProc* proc_3_1 = nullptr;
|
||||||
FilterProc* proc_3_2 = nullptr;
|
FilterProc* proc_3_2 = nullptr;
|
||||||
FilterProc* proc_3_3 = nullptr;
|
FilterProc* proc_3_3 = nullptr;
|
||||||
|
|
||||||
@ -201,27 +280,43 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
|
|||||||
switch (ct) {
|
switch (ct) {
|
||||||
case kRGBA_8888_SkColorType:
|
case kRGBA_8888_SkColorType:
|
||||||
case kBGRA_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_2 = downsample_2_2<ColorTypeFilter_8888>;
|
||||||
proc_2_3 = downsample_2_3<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_2 = downsample_3_2<ColorTypeFilter_8888>;
|
||||||
proc_3_3 = downsample_3_3<ColorTypeFilter_8888>;
|
proc_3_3 = downsample_3_3<ColorTypeFilter_8888>;
|
||||||
break;
|
break;
|
||||||
case kRGB_565_SkColorType:
|
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_2 = downsample_2_2<ColorTypeFilter_565>;
|
||||||
proc_2_3 = downsample_2_3<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_2 = downsample_3_2<ColorTypeFilter_565>;
|
||||||
proc_3_3 = downsample_3_3<ColorTypeFilter_565>;
|
proc_3_3 = downsample_3_3<ColorTypeFilter_565>;
|
||||||
break;
|
break;
|
||||||
case kARGB_4444_SkColorType:
|
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_2 = downsample_2_2<ColorTypeFilter_4444>;
|
||||||
proc_2_3 = downsample_2_3<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_2 = downsample_3_2<ColorTypeFilter_4444>;
|
||||||
proc_3_3 = downsample_3_3<ColorTypeFilter_4444>;
|
proc_3_3 = downsample_3_3<ColorTypeFilter_4444>;
|
||||||
break;
|
break;
|
||||||
case kAlpha_8_SkColorType:
|
case kAlpha_8_SkColorType:
|
||||||
case kGray_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_2 = downsample_2_2<ColorTypeFilter_8>;
|
||||||
proc_2_3 = downsample_2_3<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_2 = downsample_3_2<ColorTypeFilter_8>;
|
||||||
proc_3_3 = downsample_3_3<ColorTypeFilter_8>;
|
proc_3_3 = downsample_3_3<ColorTypeFilter_8>;
|
||||||
break;
|
break;
|
||||||
@ -231,6 +326,9 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (src.width() <= 1 && src.height() <= 1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
// whip through our loop to compute the exact size needed
|
// whip through our loop to compute the exact size needed
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
int countLevels = 0;
|
int countLevels = 0;
|
||||||
@ -238,18 +336,15 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
|
|||||||
int width = src.width();
|
int width = src.width();
|
||||||
int height = src.height();
|
int height = src.height();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
width >>= 1;
|
width = SkTMax(1, width >> 1);
|
||||||
height >>= 1;
|
height = SkTMax(1, height >> 1);
|
||||||
if (0 == width || 0 == height) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
size += SkColorTypeMinRowBytes(ct, width) * height;
|
size += SkColorTypeMinRowBytes(ct, width) * height;
|
||||||
countLevels += 1;
|
countLevels += 1;
|
||||||
|
if (1 == width && 1 == height) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (0 == countLevels) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkASSERT(countLevels == SkMipMap::ComputeLevelCount(src.width(), src.height()));
|
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) {
|
for (int i = 0; i < countLevels; ++i) {
|
||||||
FilterProc* proc;
|
FilterProc* proc;
|
||||||
if (height & 1) { // src-height is 3
|
if (height & 1) {
|
||||||
if (width & 1) { // src-width is 3
|
if (height == 1) { // src-height is 1
|
||||||
proc = proc_3_3;
|
if (width & 1) { // src-width is 3
|
||||||
} else { // src-width is 2
|
proc = proc_3_1;
|
||||||
proc = proc_2_3;
|
} 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
|
} else { // src-height is 2
|
||||||
if (width & 1) { // src-width is 3
|
if (width & 1) {
|
||||||
proc = proc_3_2;
|
if (width == 1) { // src-width is 1
|
||||||
} else { // src-width is 2
|
proc = proc_1_2;
|
||||||
|
} else { // src-width is 3
|
||||||
|
proc = proc_3_2;
|
||||||
|
}
|
||||||
|
} else { // src-width is 2
|
||||||
proc = proc_2_2;
|
proc = proc_2_2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
width >>= 1;
|
width = SkTMax(1, width >> 1);
|
||||||
height >>= 1;
|
height = SkTMax(1, height >> 1);
|
||||||
rowBytes = SkToU32(SkColorTypeMinRowBytes(ct, width));
|
rowBytes = SkToU32(SkColorTypeMinRowBytes(ct, width));
|
||||||
|
|
||||||
levels[i].fPixmap = SkPixmap(SkImageInfo::Make(width, height, ct, at), addr, rowBytes);
|
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) {
|
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
|
// OpenGL's spec requires that each mipmap level have height/width equal to
|
||||||
// max(1, floor(original_height / 2^i)
|
// max(1, floor(original_height / 2^i)
|
||||||
// (or original_width) where i is the mipmap level.
|
// (or original_width) where i is the mipmap level.
|
||||||
// Continue scaling down until both axes are size 1.
|
// 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);
|
const int largestAxis = SkTMax(baseWidth, baseHeight);
|
||||||
if (smallestAxis < 2) {
|
if (largestAxis < 2) {
|
||||||
// SkMipMap::Build requires a minimum size of 2.
|
// SkMipMap::Build requires a minimum size of 2.
|
||||||
return 0;
|
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
|
// If the value 00011010 has 3 leading 0s then it has 5 significant bits
|
||||||
// (the bits which are not leading zeros)
|
// (the bits which are not leading zeros)
|
||||||
const int significantBits = (sizeof(uint32_t) * 8) - leadingZeros;
|
const int significantBits = (sizeof(uint32_t) * 8) - leadingZeros;
|
||||||
|
@ -125,12 +125,12 @@ DEF_TEST(MipMap_ComputeLevelCount, reporter) {
|
|||||||
{100, 0, 0},
|
{100, 0, 0},
|
||||||
{0, 0, 0},
|
{0, 0, 0},
|
||||||
// 1
|
// 1
|
||||||
{1, 100, 0},
|
{1, 100, 6},
|
||||||
{100, 1, 0},
|
{100, 1, 6},
|
||||||
{1, 1, 0},
|
{1, 1, 0},
|
||||||
// 2
|
// 2
|
||||||
{2, 100, 1},
|
{2, 100, 6},
|
||||||
{100, 2, 1},
|
{100, 2, 6},
|
||||||
{2, 2, 1},
|
{2, 2, 1},
|
||||||
|
|
||||||
// Test a handful of boundaries such as 63x63 and 64x64
|
// Test a handful of boundaries such as 63x63 and 64x64
|
||||||
@ -142,9 +142,9 @@ DEF_TEST(MipMap_ComputeLevelCount, reporter) {
|
|||||||
{256, 256, 8},
|
{256, 256, 8},
|
||||||
|
|
||||||
// Test different dimensions, such as 256x64
|
// Test different dimensions, such as 256x64
|
||||||
{64, 129, 6},
|
{64, 129, 7},
|
||||||
{255, 32, 5},
|
{255, 32, 7},
|
||||||
{500, 1000, 8}
|
{500, 1000, 9}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto& currentTest : tests) {
|
for (auto& currentTest : tests) {
|
||||||
|
Loading…
Reference in New Issue
Block a user