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 <assert.h>#include <stdbool.h>#include <stdint.h>#include <stdlib.h>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)    {        for (uint y = startY; y < startY + checkerSize; ++y)        {            colorIndex = lastColor;            rowX = 0;            for (uint x = 0; x < width; ++x)            {                if (rowX == checkerSize)                {                    // Invert color every time we complete a checker square.                    colorIndex = !colorIndex;                    rowX = 0;                }                uint index = x + (y * width);                assert(index < width * height);                buffer[index] = colors[colorIndex];                ++rowX;            }        }        startY += checkerSize;        lastColor = !lastColor;    }}void fillBufferWithStripePattern(Color * buffer, uint width, uint height, uint lineThickness, bool vertical, const Color colors[2]){    assert(buffer != NULL);    assert(width  != 0);    assert(height != 0);    assert(validLineThickness(lineThickness));    uint colorIndex = 0;    if (vertical) // Vertical stripes:    {        for (uint y = 0; y < height; ++y)        {            for (uint x = 0; x < width; )            {                for (uint lineX = 0; lineX < lineThickness; ++lineX, ++x)                {                    uint index = x + (y * width);                    assert(index < width * height);                    buffer[index] = colors[colorIndex];                }                colorIndex = !colorIndex;            }        }    }    else // Horizontal stripes:    {        for (uint y = 0; y < height; y += lineThickness)        {            for (uint lineY = y; lineY < y + lineThickness; ++lineY)            {                for (uint lineX = 0; lineX < width; ++lineX)                {                    uint index = lineX + (lineY * width);                    assert(index < width * height);                    buffer[index] = colors[colorIndex];                }            }            colorIndex = !colorIndex;        }    }}void fillBufferWithBoxPattern(Color * buffer, uint width, uint height, uint lineThickness, const Color colors[2]){    assert(buffer != NULL);    assert(width  != 0);    assert(height != 0);    assert(validLineThickness(lineThickness));    uint colorIndex   = 0;    int  lineStart    = 0;    int  lineEnd      = width;    int  lineSizeIncr = lineThickness;    bool flippedSide  = false; // Starts at the top half.    // Horizontal lines:    for (uint y = 0; y < height; y += lineThickness)    {        // Do one line with the required thickness:        for (uint lineY = y; lineY < y + lineThickness; ++lineY)        {            for (int lineX = lineStart; lineX < lineEnd; ++lineX)            {                uint index = lineX + (lineY * width);                assert(index < width * height);                buffer[index] = colors[colorIndex];            }        }        colorIndex = !colorIndex;        // Switching to the bottom half of the image?        if (!flippedSide && y >= (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; ++x)    {        // Do a one pixel wide vertical line. Next iteration        // will do another to reach the required line thickness.        for (int lineY = lineStart; lineY < lineEnd; ++lineY)        {            uint index = x + (lineY * width);            assert(index < width * height);            buffer[index] = colors[colorIndex];        }        ++linePixelsDone;        if (linePixelsDone == lineThickness)        {            // Switching to the right side of the image?            if (!flippedSide && 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 * buffer = malloc(imageSize * imageSize * sizeof(Color));fillBufferWithCheckerPattern(buffer, imageSize, imageSize, 4, colors);// buffer gets used and later discarded.What I'm mainly unhappy with is the complexity of fillBufferWithBoxPattern, but I couldn't figure out a better way of creating the concentric box pattern. Overall, I think it uses way too many variables and intermediate states.The other two functions seem okay, but I do get the feeling they could also be simplified a bit, towards code clarity.Same goes for naming, not sure if in particular the variables are well named and make themselves clear.Let me know what you think and shout out any suggestions to make this code simpler and easier to understand.Note that since this code is very case specific, I did minimal error checking on purpose, mainly with simple assertions because I intend to (and can) always compile it with the checks enabled.
Simple procedural image patterns
c;graphics;c99
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 < 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];        }    }}