blob: 00a6c40f8174778824bea3390c8737082a2257fc [file] [log] [blame]
// Copyright 2006 Google Inc.
// All Rights Reserved.
// Author: <renn@google.com> (Marius Renn)
//
#include "color.h"
#include "debugging.h"
#include "helium_image.h"
#include "quicksmooth.h"
using namespace helium;
Color QuickSmooth::Sum2(const Color& a, const Color& b) {
return MakeColor(Red(a) + Red(b),
Green(a) + Green(b),
Blue(a) + Blue(b));
}
Color QuickSmooth::Sum3(const Color& a, const Color& b, const Color& c) {
return MakeColor(Red(a) + Red(b) + Red(c),
Green(a) + Green(b) + Green(c),
Blue(a) + Blue(b) + Blue(c));
}
Color QuickSmooth::Sum4(const Color& a,
const Color& b,
const Color& c,
const Color& d) {
return MakeColor(Red(a) + Red(b) + Red(c) + Red(d),
Green(a) + Green(b) + Green(c) + Green(d),
Blue(a) + Blue(b) + Blue(c) + Blue(d));
}
// Smoothes the given image by using a convolution kernel of:
//
// 1/9 1/9 1/9
// 1/9 1/9 1/9
// 1/9 1/9 1/9
//
// This is actually done in two steps. First all values are divided by
// 9, then in the second pass, all values in the kernel range are summed
// up.
// The smoothing is done almost in-place, and old values are reused where
// possible. For any pixel, that is not on an edge of the image, the
// following situation holds:
//
// r1 r2 r3
// c1 d1 x1
// c2 d2 x2,
//
// where (r1 + r2 + r3), (c1 + c2), (d1 + d2) have already been calculated.
// Only x1 and x2 need to be fetched from memory. (Actually, 3 values must be
// fetched from memory: x1, x2, and d1. This is due to the algorithm
// storing only the sums (r1 + r2 + r3), (c1 + c2) and (d1 + d2), not
// the operands themselves. Since we need to store (r1' + r2' + r3') for
// the next row, we need to calculate the sum (c1 + d1 + x1). We avoid
// having to lookup c1 again by caching the last d1).
//
// In terms of memory usage, this means we require an extra 3 values for the
// three column sums (c1 + c2), (d1 + d2), (x1 + x2), and an extra n values
// for the row sums at each horizontal offset, where n is the width of the
// image.
//
// Special cases apply for the edges, which are not discussed here, as they
// can be easily deduced from the standard case.
void QuickSmooth::SumPixels(Image& image) {
Color* img_ptr = image.Access(1, 1);
unsigned row = image.width();
Color last_value = *img_ptr;
Color row_value = 0;
Color cur_col[3];
Color cur_row[image.width()];
// First pixel of image
cur_col[0] = Sum3(*(img_ptr - row - 1),
*(img_ptr - 1),
*(img_ptr + row - 1));
cur_col[1] = Sum3(*(img_ptr - row),
*(img_ptr),
*(img_ptr + row));
cur_col[2] = Sum3(*(img_ptr - row + 1),
*(img_ptr + 1),
*(img_ptr + row + 1));
cur_row[0] = Sum3(*(img_ptr - 1),
*(img_ptr),
*(img_ptr + 1));
*img_ptr = Sum3(cur_col[0], cur_col[1], cur_col[2]);
++img_ptr;
uint8 c = 0;
// First row
for (unsigned x = 2; x < image.width() - 1; x++) {
cur_col[c] = Sum3(*(img_ptr - row + 1),
*(img_ptr + 1),
*(img_ptr + row + 1));
cur_row[x - 1] = Sum3(last_value,
*(img_ptr),
*(img_ptr + 1));
last_value = *img_ptr;
*img_ptr = Sum3(cur_col[0], cur_col[1], cur_col[2]);
++img_ptr;
c = (c == 2) ? 0 : c + 1;
}
img_ptr += 2;
// Rest of image
for (unsigned y = 2; y < image.height() - 1; y++) {
last_value = *img_ptr;
// First pixel in row
cur_col[0] = Sum2(*(img_ptr - 1),
*(img_ptr + row - 1));
cur_col[1] = Sum2(*(img_ptr),
*(img_ptr + row));
cur_col[2] = Sum2(*(img_ptr + 1),
*(img_ptr + row + 1));
row_value = Sum3(*(img_ptr - 1),
*(img_ptr),
*(img_ptr + 1));
*img_ptr = Sum4(cur_col[0], cur_col[1], cur_col[2], cur_row[0]);
cur_row[0] = row_value;
++img_ptr;
c = 0;
// All other pixels in row
Color d1, x1, x2;
for (unsigned x = 2; x < image.width() - 1; x++) {
d1 = *(img_ptr);
x1 = *(img_ptr + 1);
x2 = *(img_ptr + row + 1);
cur_col[c] = Sum2(x1, x2);
row_value = Sum3(last_value, d1, x2);
last_value = d1;
*img_ptr = Sum4(cur_col[0], cur_col[1], cur_col[2], cur_row[x - 1]);
cur_row[x - 1] = row_value;
++img_ptr;
c = (c == 2) ? 0 : c + 1;
}
img_ptr += 2;
}
}
void QuickSmooth::DividePixels(Image& image) {
Color pixel;
for (Color* img_ptr = image.data(); img_ptr < image.DataEnd(); img_ptr++) {
pixel = *img_ptr;
*img_ptr = MakeColor(Red(pixel) / 9, Green(pixel) / 9, Blue(pixel) / 9);
}
}
void QuickSmooth::Smooth(Image& image) {
DividePixels(image);
SumPixels(image);
}