Flame Effect

Source: src/flame_effect.c · 121 lines · 7 functions.

Module entry points

These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.

How to read these diagrams

Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.

These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.

Functions in this file

flameColor() · source line 11
static uint16_t flameColor(uint8_t value, const flame_palette_t *palette)
  1. Decisionvalue < 40U
    True
    1. Exit this functionreturn COLOR_BLACK;
    False

    Continue after this decision.

  2. Decisionvalue < 90U
    True
    1. Exit this functionreturn palette->cool;
    False

    Continue after this decision.

  3. Decisionvalue < 140U
    True
    1. Exit this functionreturn palette->warm;
    False

    Continue after this decision.

  4. Decisionvalue < 190U
    True
    1. Exit this functionreturn palette->hot;
    False

    Continue after this decision.

  5. Decisionvalue < 230U
    True
    1. Exit this functionreturn palette->hottest;
    False

    Continue after this decision.

  6. Exit this functionreturn palette->peak;
Read original function source
static uint16_t flameColor(uint8_t value, const flame_palette_t *palette)
{
    if (value < 40U) return COLOR_BLACK;
    if (value < 90U) return palette->cool;
    if (value < 140U) return palette->warm;
    if (value < 190U) return palette->hot;
    if (value < 230U) return palette->hottest;
    return palette->peak;
}
nextFlameRandom() · source line 21
static uint32_t nextFlameRandom(flame_effect_t *effect)
  1. Executeeffect->random_state ^= effect->random_state << 13;
  2. Executeeffect->random_state ^= effect->random_state >> 17;
  3. Executeeffect->random_state ^= effect->random_state << 5;
  4. Exit this functionreturn effect->random_state;
Read original function source
static uint32_t nextFlameRandom(flame_effect_t *effect)
{
    effect->random_state ^= effect->random_state << 13;
    effect->random_state ^= effect->random_state >> 17;
    effect->random_state ^= effect->random_state << 5;
    return effect->random_state;
}
flameEffectInitialize() · source line 29
void flameEffectInitialize(flame_effect_t *effect, uint8_t *heat, unsigned columns,
                           unsigned rows, unsigned pixel_width, unsigned pixel_height,
                           bool rises, uint32_t seed)
  1. Executeeffect->heat = heat;
  2. Executeeffect->columns = columns;
  3. Executeeffect->rows = rows;
  4. Executeeffect->pixel_width = pixel_width;
  5. Executeeffect->pixel_height = pixel_height;
  6. Executeeffect->rises = rises;
  7. Executeeffect->random_state = seed == 0U ? 0x6D2B79F5U : seed;
  8. Executememset(heat, 0, (size_t)columns * rows);
Read original function source
void flameEffectInitialize(flame_effect_t *effect, uint8_t *heat, unsigned columns,
                           unsigned rows, unsigned pixel_width, unsigned pixel_height,
                           bool rises, uint32_t seed)
{
    effect->heat = heat;
    effect->columns = columns;
    effect->rows = rows;
    effect->pixel_width = pixel_width;
    effect->pixel_height = pixel_height;
    effect->rises = rises;
    effect->random_state = seed == 0U ? 0x6D2B79F5U : seed;
    memset(heat, 0, (size_t)columns * rows);
}
flameEffectSpread() · source line 43
static void flameEffectSpread(flame_effect_t *effect)

Calls into this documentation:

  1. Decisioneffect->rises
    True
      1. FOR loopunsigned y = 0; y < effect->rows - 1U; ++y

        Repeat body while the loop condition permits; then continue below.

          1. Executeconst unsigned row = y * effect->columns;
          2. Executeconst unsigned source = row + effect->columns;
          3. FOR loopunsigned x = 0; x < effect->columns; ++x

            Repeat body while the loop condition permits; then continue below.

              1. Executeint source_x = (int)x + (int)(nextFlameRandom(effect) % 3U) - 1;
              2. Decisionsource_x < 0
                True
                1. Executesource_x = 0;
                False

                Continue after this decision.

              3. Decisionsource_x >= (int)effect->columns
                True
                1. Executesource_x = (int)effect->columns - 1;
                False

                Continue after this decision.

              4. Executeconst int cooled = effect->heat[source + (unsigned)source_x] - (int)(3U + nextFlameRandom(effect) % 7U);
              5. Executeeffect->heat[row + x] = cooled > 0 ? (uint8_t)cooled : 0U;
    False
      1. FOR loopunsigned y = effect->rows - 1U; y > 0U; --y

        Repeat body while the loop condition permits; then continue below.

          1. Executeconst unsigned row = y * effect->columns;
          2. Executeconst unsigned source = row - effect->columns;
          3. FOR loopunsigned x = 0; x < effect->columns; ++x

            Repeat body while the loop condition permits; then continue below.

              1. Executeint source_x = (int)x + (int)(nextFlameRandom(effect) % 3U) - 1;
              2. Decisionsource_x < 0
                True
                1. Executesource_x = 0;
                False

                Continue after this decision.

              3. Decisionsource_x >= (int)effect->columns
                True
                1. Executesource_x = (int)effect->columns - 1;
                False

                Continue after this decision.

              4. Executeconst int cooled = effect->heat[source + (unsigned)source_x] - (int)(3U + nextFlameRandom(effect) % 7U);
              5. Executeeffect->heat[row + x] = cooled > 0 ? (uint8_t)cooled : 0U;
Read original function source
static void flameEffectSpread(flame_effect_t *effect)
{
    if (effect->rises) {
        for (unsigned y = 0; y < effect->rows - 1U; ++y) {
            const unsigned row = y * effect->columns;
            const unsigned source = row + effect->columns;
            for (unsigned x = 0; x < effect->columns; ++x) {
                int source_x = (int)x + (int)(nextFlameRandom(effect) % 3U) - 1;
                if (source_x < 0) source_x = 0;
                if (source_x >= (int)effect->columns) source_x = (int)effect->columns - 1;
                const int cooled = effect->heat[source + (unsigned)source_x] -
                                   (int)(3U + nextFlameRandom(effect) % 7U);
                effect->heat[row + x] = cooled > 0 ? (uint8_t)cooled : 0U;
            }
        }
    } else {
        for (unsigned y = effect->rows - 1U; y > 0U; --y) {
            const unsigned row = y * effect->columns;
            const unsigned source = row - effect->columns;
            for (unsigned x = 0; x < effect->columns; ++x) {
                int source_x = (int)x + (int)(nextFlameRandom(effect) % 3U) - 1;
                if (source_x < 0) source_x = 0;
                if (source_x >= (int)effect->columns) source_x = (int)effect->columns - 1;
                const int cooled = effect->heat[source + (unsigned)source_x] -
                                   (int)(3U + nextFlameRandom(effect) % 7U);
                effect->heat[row + x] = cooled > 0 ? (uint8_t)cooled : 0U;
            }
        }
    }
}
flameEffectUpdate() · source line 74
void flameEffectUpdate(flame_effect_t *effect)

Calls into this documentation:

  1. Executeconst unsigned source_y = effect->rises ? effect->rows - 1U : 0U;
  2. Executeconst unsigned source_row = source_y * effect->columns;
  3. FOR loopunsigned x = 0; x < effect->columns; ++x

    Repeat body while the loop condition permits; then continue below.

    1. Executeeffect->heat[source_row + x] = 170U + nextFlameRandom(effect) % 86U;
  4. ExecuteflameEffectSpread(effect);
Read original function source
void flameEffectUpdate(flame_effect_t *effect)
{
    const unsigned source_y = effect->rises ? effect->rows - 1U : 0U;
    const unsigned source_row = source_y * effect->columns;
    for (unsigned x = 0; x < effect->columns; ++x)
        effect->heat[source_row + x] = 170U + nextFlameRandom(effect) % 86U;
    flameEffectSpread(effect);
}
flameEffectUpdateFromSource() · source line 83
void flameEffectUpdateFromSource(flame_effect_t *effect, const uint8_t *source_heat)

Calls into this documentation:

  1. Executeconst unsigned source_y = effect->rises ? effect->rows - 1U : 0U;
  2. Executememcpy(effect->heat + source_y * effect->columns, source_heat, effect->columns);
  3. ExecuteflameEffectSpread(effect);
Read original function source
void flameEffectUpdateFromSource(flame_effect_t *effect, const uint8_t *source_heat)
{
    const unsigned source_y = effect->rises ? effect->rows - 1U : 0U;
    memcpy(effect->heat + source_y * effect->columns, source_heat, effect->columns);
    flameEffectSpread(effect);
}
flameEffectDraw() · source line 90
void flameEffectDraw(uint16_t *frame, const flame_effect_t *effect, int x, int y,
                     const flame_palette_t *palette)

Calls into this documentation:

  1. Decisionframe == NULL || effect == NULL || effect->heat == NULL
    True
      1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisionpalette == NULL
    True
    1. Executepalette = &default_palette;
    False

    Continue after this decision.

  3. FOR loopunsigned row = 0; row < effect->rows; ++row

    Repeat body while the loop condition permits; then continue below.

      1. Executeconst int pixel_y = y + (int)(row * effect->pixel_height);
      2. FOR loopunsigned column = 0; column < effect->columns; ++column

        Repeat body while the loop condition permits; then continue below.

          1. Executeconst int pixel_x = x + (int)(column * effect->pixel_width);
          2. Executeconst uint16_t color = flameColor(effect->heat[row * effect->columns + column], palette);
          3. FOR loopunsigned pixel_row = 0; pixel_row < effect->pixel_height; ++pixel_row

            Repeat body while the loop condition permits; then continue below.

              1. Executeconst int destination_y = pixel_y + (int)pixel_row;
              2. Decisiondestination_y < 0 || destination_y >= LCD_HEIGHT
                True
                  1. Next loop iterationcontinue;
                False

                Continue after this decision.

              3. FOR loopunsigned pixel_column = 0; pixel_column < effect->pixel_width; ++pixel_column

                Repeat body while the loop condition permits; then continue below.

                  1. Executeconst int destination_x = pixel_x + (int)pixel_column;
                  2. Decisiondestination_x >= 0 && destination_x < LCD_WIDTH
                    True
                      1. Executeframe[(size_t)destination_y * LCD_WIDTH + destination_x] = color;
                    False

                    Continue after this decision.

Read original function source
void flameEffectDraw(uint16_t *frame, const flame_effect_t *effect, int x, int y,
                     const flame_palette_t *palette)
{
    if (frame == NULL || effect == NULL || effect->heat == NULL) {
        return;
    }
    if (palette == NULL) palette = &default_palette;

    for (unsigned row = 0; row < effect->rows; ++row) {
        const int pixel_y = y + (int)(row * effect->pixel_height);
        for (unsigned column = 0; column < effect->columns; ++column) {
            const int pixel_x = x + (int)(column * effect->pixel_width);
            const uint16_t color = flameColor(effect->heat[row * effect->columns + column],
                                              palette);
            for (unsigned pixel_row = 0; pixel_row < effect->pixel_height; ++pixel_row) {
                const int destination_y = pixel_y + (int)pixel_row;
                if (destination_y < 0 || destination_y >= LCD_HEIGHT) {
                    continue;
                }
                for (unsigned pixel_column = 0; pixel_column < effect->pixel_width; ++pixel_column) {
                    const int destination_x = pixel_x + (int)pixel_column;
                    // Build the framebuffer address only after both coordinates
                    // are valid.  This guards About and Hall of Fame against a
                    // damaged or changed flame field corrupting LCD state.
                    if (destination_x >= 0 && destination_x < LCD_WIDTH) {
                        frame[(size_t)destination_y * LCD_WIDTH + destination_x] = color;
                    }
                }
            }
        }
    }
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "flame_effect.h"

#include "game_graphics.h"
#include <stddef.h>
#include <string.h>

static const flame_palette_t default_palette = {
    COLOR_DARKRED, COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_WHITE,
};

static uint16_t flameColor(uint8_t value, const flame_palette_t *palette)
{
    if (value < 40U) return COLOR_BLACK;
    if (value < 90U) return palette->cool;
    if (value < 140U) return palette->warm;
    if (value < 190U) return palette->hot;
    if (value < 230U) return palette->hottest;
    return palette->peak;
}

static uint32_t nextFlameRandom(flame_effect_t *effect)
{
    effect->random_state ^= effect->random_state << 13;
    effect->random_state ^= effect->random_state >> 17;
    effect->random_state ^= effect->random_state << 5;
    return effect->random_state;
}

void flameEffectInitialize(flame_effect_t *effect, uint8_t *heat, unsigned columns,
                           unsigned rows, unsigned pixel_width, unsigned pixel_height,
                           bool rises, uint32_t seed)
{
    effect->heat = heat;
    effect->columns = columns;
    effect->rows = rows;
    effect->pixel_width = pixel_width;
    effect->pixel_height = pixel_height;
    effect->rises = rises;
    effect->random_state = seed == 0U ? 0x6D2B79F5U : seed;
    memset(heat, 0, (size_t)columns * rows);
}

static void flameEffectSpread(flame_effect_t *effect)
{
    if (effect->rises) {
        for (unsigned y = 0; y < effect->rows - 1U; ++y) {
            const unsigned row = y * effect->columns;
            const unsigned source = row + effect->columns;
            for (unsigned x = 0; x < effect->columns; ++x) {
                int source_x = (int)x + (int)(nextFlameRandom(effect) % 3U) - 1;
                if (source_x < 0) source_x = 0;
                if (source_x >= (int)effect->columns) source_x = (int)effect->columns - 1;
                const int cooled = effect->heat[source + (unsigned)source_x] -
                                   (int)(3U + nextFlameRandom(effect) % 7U);
                effect->heat[row + x] = cooled > 0 ? (uint8_t)cooled : 0U;
            }
        }
    } else {
        for (unsigned y = effect->rows - 1U; y > 0U; --y) {
            const unsigned row = y * effect->columns;
            const unsigned source = row - effect->columns;
            for (unsigned x = 0; x < effect->columns; ++x) {
                int source_x = (int)x + (int)(nextFlameRandom(effect) % 3U) - 1;
                if (source_x < 0) source_x = 0;
                if (source_x >= (int)effect->columns) source_x = (int)effect->columns - 1;
                const int cooled = effect->heat[source + (unsigned)source_x] -
                                   (int)(3U + nextFlameRandom(effect) % 7U);
                effect->heat[row + x] = cooled > 0 ? (uint8_t)cooled : 0U;
            }
        }
    }
}

void flameEffectUpdate(flame_effect_t *effect)
{
    const unsigned source_y = effect->rises ? effect->rows - 1U : 0U;
    const unsigned source_row = source_y * effect->columns;
    for (unsigned x = 0; x < effect->columns; ++x)
        effect->heat[source_row + x] = 170U + nextFlameRandom(effect) % 86U;
    flameEffectSpread(effect);
}

void flameEffectUpdateFromSource(flame_effect_t *effect, const uint8_t *source_heat)
{
    const unsigned source_y = effect->rises ? effect->rows - 1U : 0U;
    memcpy(effect->heat + source_y * effect->columns, source_heat, effect->columns);
    flameEffectSpread(effect);
}

void flameEffectDraw(uint16_t *frame, const flame_effect_t *effect, int x, int y,
                     const flame_palette_t *palette)
{
    if (frame == NULL || effect == NULL || effect->heat == NULL) {
        return;
    }
    if (palette == NULL) palette = &default_palette;

    for (unsigned row = 0; row < effect->rows; ++row) {
        const int pixel_y = y + (int)(row * effect->pixel_height);
        for (unsigned column = 0; column < effect->columns; ++column) {
            const int pixel_x = x + (int)(column * effect->pixel_width);
            const uint16_t color = flameColor(effect->heat[row * effect->columns + column],
                                              palette);
            for (unsigned pixel_row = 0; pixel_row < effect->pixel_height; ++pixel_row) {
                const int destination_y = pixel_y + (int)pixel_row;
                if (destination_y < 0 || destination_y >= LCD_HEIGHT) {
                    continue;
                }
                for (unsigned pixel_column = 0; pixel_column < effect->pixel_width; ++pixel_column) {
                    const int destination_x = pixel_x + (int)pixel_column;
                    // Build the framebuffer address only after both coordinates
                    // are valid.  This guards About and Hall of Fame against a
                    // damaged or changed flame field corrupting LCD state.
                    if (destination_x >= 0 && destination_x < LCD_WIDTH) {
                        frame[(size_t)destination_y * LCD_WIDTH + destination_x] = color;
                    }
                }
            }
        }
    }
}