HiveBrain v1.2.0
Get Started
← Back to all entries
patterncMinor

Simple procedural image patterns

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
proceduralsimpleimagepatterns

Problem

I have these functions for generating procedural debug data for another piece of code related to image processing. The three following functions generate these simple mechanical patterns, which are suitable enough for my tests:

#include 
#include 
#include 
#include 

typedef uint32_t Color;
typedef uint32_t uint;

static bool validLineThickness(uint lineThickness)
{
    // 1 is OK, otherwise must be evenly divisible by 2.
    if (lineThickness == 0) { return false; }
    return lineThickness == 1 || (lineThickness % 2) == 0;
}

void fillBufferWithCheckerPattern(Color * buffer, uint width, uint height, uint squares, const Color colors[2])
{
    assert(buffer  != NULL);
    assert(width   != 0);
    assert(height  != 0);
    assert(squares >= 2);

    // Size of one checker square, in pixels.
    uint checkerSize = width / squares;

    uint startY     = 0;
    uint lastColor  = 0;
    uint colorIndex = 0;
    uint rowX       = 0;

    while (startY = (height / 2))
        {
            lineSizeIncr = -lineSizeIncr;
            colorIndex   = !colorIndex;
            flippedSide  = true;
        }

        lineStart += lineSizeIncr;
        lineEnd   -= lineSizeIncr;
    }

    colorIndex   = 0;
    lineStart    = 0;
    lineEnd      = height;
    lineSizeIncr = lineThickness;
    flippedSide  = false; // Starts at the left side.

    // Vertical lines:
    uint linePixelsDone = 0;
    for (uint x = 0; x = (width / 2) - 1)
            {
                lineSizeIncr = -lineSizeIncr;
                flippedSide  = true;
            }
            else
            {
                lineStart += lineSizeIncr;
                lineEnd   -= lineSizeIncr;
                colorIndex = !colorIndex;
            }

            linePixelsDone = 0;
        }
    }
}


A tiny and out of context usage example (error checking ignored):

```
const Color colors[] =
{
RGBA(255, 0, 0, 255), // red
RGBA(255, 255, 0, 255) // yellow
};

const uint imageSize = 64;
Color * b

Solution

Always look for a way to encode things explicitly as data, or patterns in data, instead of encoding them implicitly in the control flow of your program. For example, your function to color the squares in a checkerboard pattern could have been written like this, using "(x + y) % 2" to encode the idea of "alternating colors", instead of your mess of nested loops and counter variables.

void fillBufferWithCheckerPattern(
    Color *buffer, int width, int height, int squares, const Color colors[2])
{
    assert(buffer != NULL);
    assert(squares >= 2);

    const int checkerSize = width / squares;

    assert(width > 0 && width % squares == 0);
    assert(height > 0 && height % squares == 0);

    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            const int colorIndex = ((y / checkerSize) + (x / checkerSize)) % 2;
            buffer[y*width+x] = colors[colorIndex];
        }
    }
}


Notice also that I've dropped the u from your uint, which means I can also get rid of your typedef — several lines of code saved, with no loss of performance or expressiveness. (And on top of all that: if somebody does accidentally pass width = -1 to this function, they'll get a nice assertion failure instead of a segfault.) The built-in types are nice; use them, wherever possible.

Similarly, for the "concentric boxes" pattern, what you're doing is coloring each pixel according to its distance from the edge of the grid. Pixels at distance 1 get red; pixels at distance 2 get yellow; pixels at distance 3 get red again; and so on, all the way to the middle.

static inline int min2(int a, int b) { return a  0);
    assert(height > 0);

    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            // Is it closer to the top, bottom, left, or right?
            const int mindist = min4(
                x,  // distance from left side
                y,  // distance from top
                width-x-1,  // distance from right side
                height-y-1  // distance from bottom
            );
            const int colorIndex = (mindist / lineThickness) % 2;
            buffer[y*width+x] = colors[colorIndex];
        }
    }
}

Code Snippets

void fillBufferWithCheckerPattern(
    Color *buffer, int width, int height, int squares, const Color colors[2])
{
    assert(buffer != NULL);
    assert(squares >= 2);

    const int checkerSize = width / squares;

    assert(width > 0 && width % squares == 0);
    assert(height > 0 && height % squares == 0);

    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            const int colorIndex = ((y / checkerSize) + (x / checkerSize)) % 2;
            buffer[y*width+x] = colors[colorIndex];
        }
    }
}
static inline int min2(int a, int b) { return a < b ? a : b; }

static inline int min4(int a, int b, int c, int d)
{
    return min2(a, min2(b, min2(c, d)));
}

void fillBufferWithBoxPattern(
    Color *buffer, int width, int height,
    int lineThickness, const Color colors[2])
{
    assert(buffer != NULL);
    assert(width > 0);
    assert(height > 0);

    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            // Is it closer to the top, bottom, left, or right?
            const int mindist = min4(
                x,  // distance from left side
                y,  // distance from top
                width-x-1,  // distance from right side
                height-y-1  // distance from bottom
            );
            const int colorIndex = (mindist / lineThickness) % 2;
            buffer[y*width+x] = colors[colorIndex];
        }
    }
}

Context

StackExchange Code Review Q#96738, answer score: 5

Revisions (0)

No revisions yet.