Avoid copying error buffers around

# Conflicts:
#	src/render/error_diffusion.cpp
This commit is contained in:
David Thomas 2025-06-13 14:27:28 +01:00
parent d6129359c4
commit b43e3e6ac1
No known key found for this signature in database
GPG Key ID: 553E822E460EE293
2 changed files with 23 additions and 15 deletions

View File

@ -147,6 +147,7 @@ void ErrorDiffusionDither::start(const doc::Image* srcImage,
m_err[i].resize(bufferSize, 0); m_err[i].resize(bufferSize, 0);
m_lastY = -1; m_lastY = -1;
m_currentRowOffset = 0;
m_factor = int(factor * 100.0); m_factor = int(factor * 100.0);
} }
@ -162,17 +163,17 @@ doc::color_t ErrorDiffusionDither::ditherRgbToIndex2D(const int x,
const ErrorDiffusionMatrix& matrix = getCurrentMatrix(); const ErrorDiffusionMatrix& matrix = getCurrentMatrix();
if (y != m_lastY) { if (y != m_lastY) {
for (int i = 0; i < kChannels; ++i) { // Instead of shifting all rows, just advance the circular buffer
// Shift error rows up // and clear the row that will be reused
for (int row = 0; row < matrix.height - 1; ++row) { m_currentRowOffset = (m_currentRowOffset + 1) % matrix.height;
int* srcRow = &m_err[i][m_width * (row + 1)];
int* dstRow = &m_err[i][m_width * row]; // Clear only the row that will be used as the "last" row
std::copy(srcRow, srcRow + m_width, dstRow); int clearRowIndex = (m_currentRowOffset + matrix.height - 1) % matrix.height;
} for (int c = 0; c < kChannels; ++c) {
// Clear the last row int* rowToClear = &m_err[c][m_width * clearRowIndex];
int* lastRow = &m_err[i][m_width * (matrix.height - 1)]; std::fill(rowToClear, rowToClear + m_width, 0);
std::fill(lastRow, lastRow + m_width, 0);
} }
m_lastY = y; m_lastY = y;
} }
@ -185,8 +186,10 @@ doc::color_t ErrorDiffusionDither::ditherRgbToIndex2D(const int x,
doc::rgba_geta(color) }; doc::rgba_geta(color) };
// Add accumulated error (16-bit fixed point) and convert to 0..255 // Add accumulated error (16-bit fixed point) and convert to 0..255
for (int i = 0; i < kChannels; ++i) for (int c = 0; c < kChannels; ++c)
v[i] = std::clamp(((v[i] << 16) + m_err[i][x + 1] + 32767) >> 16, 0, 255); v[c] = std::clamp(((v[c] << 16) + m_err[c][m_width * m_currentRowOffset + x + 1] + 32767) >> 16,
0,
255);
const doc::color_t index = (rgbmap ? const doc::color_t index = (rgbmap ?
rgbmap->mapColor(v[0], v[1], v[2], v[3]) : rgbmap->mapColor(v[0], v[1], v[2], v[3]) :
@ -206,10 +209,14 @@ doc::color_t ErrorDiffusionDither::ditherRgbToIndex2D(const int x,
const int srcWidth = m_srcImage->width(); const int srcWidth = m_srcImage->width();
// Distribute error using the configurable matrix // Distribute error using the configurable matrix
for (int i = 0; i < kChannels; ++i) { for (int c = 0; c < kChannels; ++c) {
const int qerr = quantError[i] * m_factor / 100; const int qerr = quantError[c] * m_factor / 100;
for (int my = 0; my < matrix.height; ++my) { for (int my = 0; my < matrix.height; ++my) {
// Use circular buffer indexing
int bufferRow = (m_currentRowOffset + my) % matrix.height; // hoist
int bufferRowIndex = bufferRow * m_width;
for (int mx = 0; mx < matrix.width; ++mx) { for (int mx = 0; mx < matrix.width; ++mx) {
const int coeff = matrix.coefficients[my][mx]; const int coeff = matrix.coefficients[my][mx];
if (coeff == 0) if (coeff == 0)
@ -229,7 +236,7 @@ doc::color_t ErrorDiffusionDither::ditherRgbToIndex2D(const int x,
const int bufferRow = my; const int bufferRow = my;
const int bufferIndex = bufferRow * m_width + errorPixelX + 1; const int bufferIndex = bufferRow * m_width + errorPixelX + 1;
m_err[i][bufferIndex] += errorValue; m_err[c][bufferRowIndex + errorPixelX + 1] += errorValue;
} }
} }
} }

View File

@ -66,6 +66,7 @@ private:
ErrorDiffusionType m_diffusionType; ErrorDiffusionType m_diffusionType;
const doc::Image* m_srcImage; const doc::Image* m_srcImage;
int m_width, m_lastY; int m_width, m_lastY;
int m_currentRowOffset;
static const int kChannels = 4; static const int kChannels = 4;
std::vector<int> m_err[kChannels]; std::vector<int> m_err[kChannels];
int m_factor; int m_factor;