Use dither radius based on image size.
This commit is contained in:
parent
3d5fcdb7dd
commit
3b45189923
1 changed files with 43 additions and 23 deletions
|
|
@ -583,57 +583,58 @@ QImage BlurLargeImage(QImage image, int radius) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QImage DitherImage(QImage image) {
|
template <int kBits> // 4 means 16x16, 3 means 8x8
|
||||||
Expects(image.bytesPerLine() == image.width() * 4);
|
[[nodiscard]] QImage DitherGeneric(const QImage &image) {
|
||||||
|
static_assert(kBits >= 1 && kBits <= 4);
|
||||||
|
|
||||||
|
constexpr auto kSquareSide = (1 << kBits);
|
||||||
|
constexpr auto kShift = kSquareSide / 2;
|
||||||
|
constexpr auto kMask = (kSquareSide - 1);
|
||||||
|
|
||||||
const auto width = image.width();
|
const auto width = image.width();
|
||||||
const auto height = image.height();
|
const auto height = image.height();
|
||||||
|
|
||||||
if (width < 16 || height < 16) {
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto area = width * height;
|
const auto area = width * height;
|
||||||
const auto shifts = std::make_unique<uchar[]>(area);
|
const auto shifts = std::make_unique<uchar[]>(area);
|
||||||
bytes::set_random(bytes::make_span(shifts.get(), area));
|
bytes::set_random(bytes::make_span(shifts.get(), area));
|
||||||
|
|
||||||
// shiftx = int(shift & 0x0F) - 8; shifty = int(shift >> 4) - 8;
|
// shiftx = int(shift & kMask) - kShift;
|
||||||
|
// shifty = int((shift >> 4) & kMask) - kShift;
|
||||||
// Clamp shifts close to edges.
|
// Clamp shifts close to edges.
|
||||||
for (auto y = 0; y != 8; ++y) {
|
for (auto y = 0; y != kShift; ++y) {
|
||||||
const auto min = 8 - y;
|
const auto min = kShift - y;
|
||||||
const auto shifted = (min << 4);
|
const auto shifted = (min << 4);
|
||||||
auto shift = shifts.get() + y * width;
|
auto shift = shifts.get() + y * width;
|
||||||
for (const auto till = shift + width; shift != till; ++shift) {
|
for (const auto till = shift + width; shift != till; ++shift) {
|
||||||
if ((*shift >> 4) < min) {
|
if (((*shift >> 4) & kMask) < min) {
|
||||||
*shift = shifted | (*shift & 0x0F);
|
*shift = shifted | (*shift & 0x0F);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto y = height - 7; y != height; ++y) {
|
for (auto y = height - (kShift - 1); y != height; ++y) {
|
||||||
const auto max = 8 + (height - y - 1);
|
const auto max = kShift + (height - y - 1);
|
||||||
const auto shifted = (max << 4);
|
const auto shifted = (max << 4);
|
||||||
auto shift = shifts.get() + y * width;
|
auto shift = shifts.get() + y * width;
|
||||||
for (const auto till = shift + width; shift != till; ++shift) {
|
for (const auto till = shift + width; shift != till; ++shift) {
|
||||||
if ((*shift >> 4) > max) {
|
if (((*shift >> 4) & kMask) > max) {
|
||||||
*shift = shifted | (*shift & 0x0F);
|
*shift = shifted | (*shift & 0x0F);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto shift = shifts.get(), ytill = shift + area
|
for (auto shift = shifts.get(), ytill = shift + area
|
||||||
; shift != ytill
|
; shift != ytill
|
||||||
; shift += width - 8) {
|
; shift += width - kShift) {
|
||||||
for (const auto till = shift + 8; shift != till; ++shift) {
|
for (const auto till = shift + kShift; shift != till; ++shift) {
|
||||||
const auto min = (till - shift);
|
const auto min = (till - shift);
|
||||||
if ((*shift & 0x0F) < min) {
|
if ((*shift & kMask) < min) {
|
||||||
*shift = (*shift & 0xF0) | min;
|
*shift = (*shift & 0xF0) | min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto shift = shifts.get(), ytill = shift + area; shift != ytill;) {
|
for (auto shift = shifts.get(), ytill = shift + area; shift != ytill;) {
|
||||||
shift += width - 7;
|
shift += width - (kShift - 1);
|
||||||
for (const auto till = shift + 7; shift != till; ++shift) {
|
for (const auto till = shift + (kShift - 1); shift != till; ++shift) {
|
||||||
const auto max = 8 + (till - shift - 1);
|
const auto max = kShift + (till - shift - 1);
|
||||||
if ((*shift & 0x0F) > max) {
|
if ((*shift & kMask) > max) {
|
||||||
*shift = (*shift & 0xF0) | max;
|
*shift = (*shift & 0xF0) | max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -646,14 +647,33 @@ QImage BlurLargeImage(QImage image, int radius) {
|
||||||
const auto dst = reinterpret_cast<uint32*>(result.bits());
|
const auto dst = reinterpret_cast<uint32*>(result.bits());
|
||||||
for (auto index = 0; index != area; ++index) {
|
for (auto index = 0; index != area; ++index) {
|
||||||
const auto shift = shifts[index];
|
const auto shift = shifts[index];
|
||||||
const auto shiftx = int(shift & 0x0F) - 8;
|
const auto shiftx = int(shift & kMask) - kShift;
|
||||||
const auto shifty = int(shift >> 4) - 8;
|
const auto shifty = int((shift >> 4) & kMask) - kShift;
|
||||||
dst[index] = src[index + (shifty * width) + shiftx];
|
dst[index] = src[index + (shifty * width) + shiftx];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QImage DitherImage(QImage image) {
|
||||||
|
Expects(image.bytesPerLine() == image.width() * 4);
|
||||||
|
|
||||||
|
const auto width = image.width();
|
||||||
|
const auto height = image.height();
|
||||||
|
const auto min = std::min(width, height);
|
||||||
|
const auto max = std::max(width, height);
|
||||||
|
if (max >= 1024 && min >= 512) {
|
||||||
|
return DitherGeneric<4>(image);
|
||||||
|
} else if (max >= 512 && min >= 256) {
|
||||||
|
return DitherGeneric<3>(image);
|
||||||
|
} else if (max >= 256 && min >= 128) {
|
||||||
|
return DitherGeneric<2>(image);
|
||||||
|
} else if (min >= 32) {
|
||||||
|
return DitherGeneric<1>(image);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
void prepareCircle(QImage &img) {
|
void prepareCircle(QImage &img) {
|
||||||
Assert(!img.isNull());
|
Assert(!img.isNull());
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue