Game Graphics
Source: src/game_graphics.c · 874 lines · 38 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
graphics_text_effect_calculate() · source line 9
void graphics_text_effect_calculate(graphics_text_effect_t effect, uint32_t elapsed_ms,
unsigned text_length,
const graphics_text_effect_params_t *params,
uint16_t base_color,
const uint16_t *colour_cycle,
unsigned colour_cycle_count,
graphics_text_effect_state_t *state)- Decision
state == NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
*state = (graphics_text_effect_state_t){ .visible_characters = text_length, .color = base_color, .scale = 1.0f, }; - Decision
params == NULL || effect >= GRAPHICS_TEXT_EFFECT_COUNTTrue- Exit this function
return;
FalseContinue after this decision.
- Select a case
effectEnter matching case; follow source fall-through until a break, return or end.
- Case / label
case GRAPHICS_TEXT_EFFECT_TRAVEL_LEFT: - Decision
params->travel_distance > 0True- Execute
const unsigned cycle = (unsigned)params->travel_distance * 2U; - Execute
state->x_offset = params->travel_distance - (int)((elapsed_ms * 55U / 1000U) % cycle);
FalseContinue after this decision.
- Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_DRIFT: - Decision
params->drift_distance > 0True- Execute
const unsigned cycle = (unsigned)params->drift_distance * 2U; - Execute
state->x_offset = (int)((elapsed_ms * 55U / 1000U) % cycle) - params->drift_distance;
FalseContinue after this decision.
- Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_BOB: - Execute
state->y_offset = (int)(sinf((float)elapsed_ms / 445.0f) * (float)params->vertical_amplitude); - Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_WAVE: - Execute
state->wave = true; - Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_JITTER: - Decision
params->jitter_amplitude > 0True- Execute
const unsigned range = (unsigned)params->jitter_amplitude * 2U + 1U; - Execute
state->x_offset = (int)((elapsed_ms / 18U) % range) - params->jitter_amplitude;
FalseContinue after this decision.
- Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_REVEAL: - Execute
state->visible_characters = (elapsed_ms / 130U) % (text_length + 1U); - Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_COLOUR_CYCLE: - Decision
colour_cycle != NULL && colour_cycle_count > 0UTrue- Execute
state->color = colour_cycle[(elapsed_ms / 180U) % colour_cycle_count];
FalseContinue after this decision.
- Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_SWING: - Execute
state->angle_degrees = sinf((float)elapsed_ms / 382.0f) * params->maximum_angle_degrees; - Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_PULSE: - Decision
params->minimum_scale > 0.0f && params->maximum_scale >= params->minimum_scaleTrue- Execute
const float progress = (sinf((float)elapsed_ms / 318.0f) + 1.0f) * 0.5f; - Execute
state->scale = params->minimum_scale + (params->maximum_scale - params->minimum_scale) * progress;
FalseContinue after this decision.
- Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_DROP: - Decision
params->drop_height > 0True- Execute
const uint32_t drop_elapsed = elapsed_ms % 2800U; - Execute
const int travelled = (int)(drop_elapsed * (uint32_t)params->drop_height / 700U); - Execute
state->y_offset = params->drop_height - (travelled < params->drop_height ? travelled : params->drop_height);
FalseContinue after this decision.
- Exit enclosing loop or switch
break; - Case / label
case GRAPHICS_TEXT_EFFECT_TYPEWRITER: - Execute
state->visible_characters = (elapsed_ms / 90U) % (text_length + 1U); - Exit enclosing loop or switch
break; - Case / label
default: - Exit enclosing loop or switch
break;
Read original function source
void graphics_text_effect_calculate(graphics_text_effect_t effect, uint32_t elapsed_ms,
unsigned text_length,
const graphics_text_effect_params_t *params,
uint16_t base_color,
const uint16_t *colour_cycle,
unsigned colour_cycle_count,
graphics_text_effect_state_t *state)
{
if (state == NULL) return;
*state = (graphics_text_effect_state_t){
.visible_characters = text_length,
.color = base_color,
.scale = 1.0f,
};
if (params == NULL || effect >= GRAPHICS_TEXT_EFFECT_COUNT) return;
switch (effect) {
case GRAPHICS_TEXT_EFFECT_TRAVEL_LEFT:
if (params->travel_distance > 0) {
const unsigned cycle = (unsigned)params->travel_distance * 2U;
state->x_offset = params->travel_distance -
(int)((elapsed_ms * 55U / 1000U) % cycle);
}
break;
case GRAPHICS_TEXT_EFFECT_DRIFT:
if (params->drift_distance > 0) {
const unsigned cycle = (unsigned)params->drift_distance * 2U;
state->x_offset = (int)((elapsed_ms * 55U / 1000U) % cycle) -
params->drift_distance;
}
break;
case GRAPHICS_TEXT_EFFECT_BOB:
state->y_offset = (int)(sinf((float)elapsed_ms / 445.0f) *
(float)params->vertical_amplitude);
break;
case GRAPHICS_TEXT_EFFECT_WAVE:
state->wave = true;
break;
case GRAPHICS_TEXT_EFFECT_JITTER:
if (params->jitter_amplitude > 0) {
const unsigned range = (unsigned)params->jitter_amplitude * 2U + 1U;
state->x_offset = (int)((elapsed_ms / 18U) % range) -
params->jitter_amplitude;
}
break;
case GRAPHICS_TEXT_EFFECT_REVEAL:
state->visible_characters = (elapsed_ms / 130U) % (text_length + 1U);
break;
case GRAPHICS_TEXT_EFFECT_COLOUR_CYCLE:
if (colour_cycle != NULL && colour_cycle_count > 0U) {
state->color = colour_cycle[(elapsed_ms / 180U) % colour_cycle_count];
}
break;
case GRAPHICS_TEXT_EFFECT_SWING:
state->angle_degrees = sinf((float)elapsed_ms / 382.0f) *
params->maximum_angle_degrees;
break;
case GRAPHICS_TEXT_EFFECT_PULSE:
if (params->minimum_scale > 0.0f &&
params->maximum_scale >= params->minimum_scale) {
const float progress = (sinf((float)elapsed_ms / 318.0f) + 1.0f) * 0.5f;
state->scale = params->minimum_scale +
(params->maximum_scale - params->minimum_scale) * progress;
}
break;
case GRAPHICS_TEXT_EFFECT_DROP:
if (params->drop_height > 0) {
const uint32_t drop_elapsed = elapsed_ms % 2800U;
const int travelled = (int)(drop_elapsed * (uint32_t)params->drop_height / 700U);
state->y_offset = params->drop_height -
(travelled < params->drop_height ? travelled : params->drop_height);
}
break;
case GRAPHICS_TEXT_EFFECT_TYPEWRITER:
state->visible_characters = (elapsed_ms / 90U) % (text_length + 1U);
break;
default:
break;
}
}graphics_text_effect_wave_offset() · source line 91
int graphics_text_effect_wave_offset(uint32_t elapsed_ms, unsigned character_index,
int wave_amplitude)- Decision
wave_amplitude <= 0True- Exit this function
return 0;
FalseContinue after this decision.
- Execute
const float phase = ((float)(elapsed_ms % 900U) / 900.0f) * 6.2831853f; - Exit this function
return (int)(sinf(phase + (float)character_index * 0.55f) * (float)wave_amplitude);
Read original function source
int graphics_text_effect_wave_offset(uint32_t elapsed_ms, unsigned character_index,
int wave_amplitude)
{
if (wave_amplitude <= 0) return 0;
const float phase = ((float)(elapsed_ms % 900U) / 900.0f) * 6.2831853f;
return (int)(sinf(phase + (float)character_index * 0.55f) *
(float)wave_amplitude);
}clear() · source line 100
void clear(uint16_t *frame, uint16_t color)
- FOR loop
size_t pixel = 0; pixel < LCD_WIDTH * LCD_HEIGHT; ++pixelRepeat body while the loop condition permits; then continue below.
- Execute
frame[pixel] = color;
Read original function source
void clear(uint16_t *frame, uint16_t color)
{
for (size_t pixel = 0; pixel < LCD_WIDTH * LCD_HEIGHT; ++pixel) {
frame[pixel] = color;
}
}fill_rect() · source line 107
void fill_rect(uint16_t *frame, int x, int y, int width, int height,
uint16_t color)- FOR loop
int row = y; row < y + height; ++rowRepeat body while the loop condition permits; then continue below.
- FOR loop
int column = x; column < x + width; ++columnRepeat body while the loop condition permits; then continue below.
- Decision
column >= 0 && column < LCD_WIDTH && row >= 0 && row < LCD_HEIGHTTrue- Execute
frame[(size_t)row * LCD_WIDTH + column] = color;
FalseContinue after this decision.
Read original function source
void fill_rect(uint16_t *frame, int x, int y, int width, int height,
uint16_t color)
{
for (int row = y; row < y + height; ++row) {
for (int column = x; column < x + width; ++column) {
if (column >= 0 && column < LCD_WIDTH && row >= 0 && row < LCD_HEIGHT) {
frame[(size_t)row * LCD_WIDTH + column] = color;
}
}
}
}draw_rolling_raster_band() · source line 119
void draw_rolling_raster_band(uint16_t *frame, int band_top, int block_height,
int band_height, uint32_t elapsed_ms,
uint32_t colour_roll_ms,
const graphics_raster_band_palette_t *palette)Calls into this documentation:
- Decision
frame == NULL || palette == NULL || palette->colours == NULL || palette->colour_count == 0U || block_height <= 0 || band_height <= 0 || colour_roll_ms == 0UTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
const int stripe_count = (block_height + band_height - 1) / band_height; - Execute
const int colour_roll_offset = (int)(elapsed_ms / colour_roll_ms) % stripe_count; - FOR loop
int y = band_top, stripe = 0; y < band_top + block_height; y += band_height, ++stripeRepeat body while the loop condition permits; then continue below.
- Execute
const int rolled = (stripe + colour_roll_offset) % stripe_count; - Execute
const int palette_index = stripe_count > 1 ? rolled * ((int)palette->colour_count - 1) / (stripe_count - 1) : 0; - Execute
const int visible_height = y + band_height <= band_top + block_height ? band_height : band_top + block_height - y; - Execute
fill_rect(frame, 0, y, LCD_WIDTH, visible_height, palette->colours[palette_index]);
Read original function source
void draw_rolling_raster_band(uint16_t *frame, int band_top, int block_height,
int band_height, uint32_t elapsed_ms,
uint32_t colour_roll_ms,
const graphics_raster_band_palette_t *palette)
{
if (frame == NULL || palette == NULL || palette->colours == NULL ||
palette->colour_count == 0U || block_height <= 0 || band_height <= 0 ||
colour_roll_ms == 0U) return;
const int stripe_count = (block_height + band_height - 1) / band_height;
const int colour_roll_offset = (int)(elapsed_ms / colour_roll_ms) % stripe_count;
for (int y = band_top, stripe = 0; y < band_top + block_height;
y += band_height, ++stripe) {
const int rolled = (stripe + colour_roll_offset) % stripe_count;
const int palette_index = stripe_count > 1 ?
rolled * ((int)palette->colour_count - 1) / (stripe_count - 1) : 0;
const int visible_height = y + band_height <= band_top + block_height ?
band_height : band_top + block_height - y;
fill_rect(frame, 0, y, LCD_WIDTH, visible_height,
palette->colours[palette_index]);
}
}draw_scaled_image() · source line 142
static void draw_scaled_image(uint16_t *frame, int x, int y,
const uint16_t *pixels, const uint8_t *mask,
int source_width, int source_height,
int draw_width, int draw_height, uint16_t color)- Decision
frame == NULL || source_width <= 0 || source_height <= 0 || draw_width <= 0 || draw_height <= 0True- Exit this function
return;
FalseContinue after this decision.
- Execute
const size_t mask_stride = ((size_t)source_width + 7U) / 8U; - Decision
(size_t)source_height > SIZE_MAX / mask_stride || (pixels != NULL && (size_t)source_height > SIZE_MAX / sizeof(*pixels) / (size_t)source_width)True- Exit this function
return;
FalseContinue after this decision.
- Execute
const int64_t right = (int64_t)x + draw_width; - Execute
const int64_t bottom = (int64_t)y + draw_height; - Decision
right <= 0 || bottom <= 0 || x >= LCD_WIDTH || y >= LCD_HEIGHTTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
const int left = x < 0 ? 0 : x; - Execute
const int top = y < 0 ? 0 : y; - Execute
const int end_x = right > LCD_WIDTH ? LCD_WIDTH : (int)right; - Execute
const int end_y = bottom > LCD_HEIGHT ? LCD_HEIGHT : (int)bottom; - FOR loop
int row = top; row < end_y; ++rowRepeat body while the loop condition permits; then continue below.
- Execute
const size_t source_y = (size_t)(((int64_t)row - y) * source_height / draw_height); - FOR loop
int column = left; column < end_x; ++columnRepeat body while the loop condition permits; then continue below.
- Execute
const size_t source_x = (size_t)(((int64_t)column - x) * source_width / draw_width); - Decision
mask != NULL && (mask[source_y * mask_stride + source_x / 8U] & (1U << (7U - source_x % 8U))) == 0True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
frame[(size_t)row * LCD_WIDTH + column] = pixels != NULL ? pixels[source_y * (size_t)source_width + source_x] : color;
Read original function source
static void draw_scaled_image(uint16_t *frame, int x, int y,
const uint16_t *pixels, const uint8_t *mask,
int source_width, int source_height,
int draw_width, int draw_height, uint16_t color)
{
if (frame == NULL || source_width <= 0 || source_height <= 0 ||
draw_width <= 0 || draw_height <= 0) {
return;
}
const size_t mask_stride = ((size_t)source_width + 7U) / 8U;
if ((size_t)source_height > SIZE_MAX / mask_stride ||
(pixels != NULL &&
(size_t)source_height > SIZE_MAX / sizeof(*pixels) / (size_t)source_width)) {
return;
}
// Clip the destination before sampling; retain the original image origin.
const int64_t right = (int64_t)x + draw_width;
const int64_t bottom = (int64_t)y + draw_height;
if (right <= 0 || bottom <= 0 || x >= LCD_WIDTH || y >= LCD_HEIGHT) {
return;
}
const int left = x < 0 ? 0 : x;
const int top = y < 0 ? 0 : y;
const int end_x = right > LCD_WIDTH ? LCD_WIDTH : (int)right;
const int end_y = bottom > LCD_HEIGHT ? LCD_HEIGHT : (int)bottom;
for (int row = top; row < end_y; ++row) {
const size_t source_y = (size_t)(((int64_t)row - y) * source_height / draw_height);
for (int column = left; column < end_x; ++column) {
const size_t source_x = (size_t)(((int64_t)column - x) * source_width / draw_width);
if (mask != NULL &&
(mask[source_y * mask_stride + source_x / 8U] &
(1U << (7U - source_x % 8U))) == 0) {
continue;
}
frame[(size_t)row * LCD_WIDTH + column] = pixels != NULL
? pixels[source_y * (size_t)source_width + source_x] : color;
}
}
}draw_bitmap_scaled() · source line 185
void draw_bitmap_scaled(uint16_t *frame, int x, int y,
const uint8_t *bitmap, int source_width, int source_height,
int draw_width, int draw_height, uint16_t color)Calls into this documentation:
- Decision
bitmap == NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
draw_scaled_image(frame, x, y, NULL, bitmap, source_width, source_height, draw_width, draw_height, color);
Read original function source
void draw_bitmap_scaled(uint16_t *frame, int x, int y,
const uint8_t *bitmap, int source_width, int source_height,
int draw_width, int draw_height, uint16_t color)
{
if (bitmap == NULL) {
return;
}
draw_scaled_image(frame, x, y, NULL, bitmap, source_width, source_height,
draw_width, draw_height, color);
}draw_rgb565_scaled() · source line 196
void draw_rgb565_scaled(uint16_t *frame, int x, int y,
const uint16_t *pixels, const uint8_t *mask,
int source_width, int source_height,
int draw_width, int draw_height)Calls into this documentation:
- Decision
pixels == NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
draw_scaled_image(frame, x, y, pixels, mask, source_width, source_height, draw_width, draw_height, 0);
Read original function source
void draw_rgb565_scaled(uint16_t *frame, int x, int y,
const uint16_t *pixels, const uint8_t *mask,
int source_width, int source_height,
int draw_width, int draw_height)
{
if (pixels == NULL) {
return;
}
draw_scaled_image(frame, x, y, pixels, mask, source_width, source_height,
draw_width, draw_height, 0);
}draw_transformed_image() · source line 208
static void draw_transformed_image(uint16_t *frame, int destination_centre_x,
int destination_centre_y,
const uint16_t *pixels, const uint8_t *mask,
int source_width, int source_height,
float scale, float angle_degrees, uint16_t color)- Decision
frame == NULL || source_width <= 0 || source_height <= 0 || scale <= 0.0f || !isfinite(scale) || !isfinite(angle_degrees)True- Exit this function
return;
FalseContinue after this decision.
- Execute
const float radians = angle_degrees * 0.01745329252f; - Execute
const float cosine = cosf(radians); - Execute
const float sine = sinf(radians); - Execute
const float half_width = (fabsf(cosine) * source_width + fabsf(sine) * source_height) * scale * 0.5f + 1.0f; - Execute
const float half_height = (fabsf(sine) * source_width + fabsf(cosine) * source_height) * scale * 0.5f + 1.0f; - Execute
const int start_x = (int)floorf(destination_centre_x - half_width); - Execute
const int end_x = (int)ceilf(destination_centre_x + half_width); - Execute
const int start_y = (int)floorf(destination_centre_y - half_height); - Execute
const int end_y = (int)ceilf(destination_centre_y + half_height); - Execute
const size_t mask_stride = ((size_t)source_width + 7U) / 8U; - Execute
const float source_centre_x = ((float)source_width - 1.0f) * 0.5f; - Execute
const float source_centre_y = ((float)source_height - 1.0f) * 0.5f; - FOR loop
int destination_y = start_y; destination_y <= end_y; ++destination_yRepeat body while the loop condition permits; then continue below.
- Decision
destination_y < 0 || destination_y >= LCD_HEIGHTTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
const float y = (float)(destination_y - destination_centre_y); - FOR loop
int destination_x = start_x; destination_x <= end_x; ++destination_xRepeat body while the loop condition permits; then continue below.
- Decision
destination_x < 0 || destination_x >= LCD_WIDTHTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
const float x = (float)(destination_x - destination_centre_x); - Execute
const int source_x = (int)floorf((cosine * x + sine * y) / scale + source_centre_x + 0.5f); - Execute
const int source_y = (int)floorf((-sine * x + cosine * y) / scale + source_centre_y + 0.5f); - Decision
source_x < 0 || source_x >= source_width || source_y < 0 || source_y >= source_heightTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Decision
mask != NULL && (mask[(size_t)source_y * mask_stride + (unsigned)source_x / 8U] & (1U << (7U - (unsigned)source_x % 8U))) == 0True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
frame[(size_t)destination_y * LCD_WIDTH + destination_x] = pixels != NULL ? pixels[(size_t)source_y * source_width + source_x] : color;
Read original function source
static void draw_transformed_image(uint16_t *frame, int destination_centre_x,
int destination_centre_y,
const uint16_t *pixels, const uint8_t *mask,
int source_width, int source_height,
float scale, float angle_degrees, uint16_t color)
{
if (frame == NULL || source_width <= 0 || source_height <= 0 ||
scale <= 0.0f || !isfinite(scale) || !isfinite(angle_degrees)) {
return;
}
const float radians = angle_degrees * 0.01745329252f;
const float cosine = cosf(radians);
const float sine = sinf(radians);
const float half_width = (fabsf(cosine) * source_width +
fabsf(sine) * source_height) * scale * 0.5f + 1.0f;
const float half_height = (fabsf(sine) * source_width +
fabsf(cosine) * source_height) * scale * 0.5f + 1.0f;
const int start_x = (int)floorf(destination_centre_x - half_width);
const int end_x = (int)ceilf(destination_centre_x + half_width);
const int start_y = (int)floorf(destination_centre_y - half_height);
const int end_y = (int)ceilf(destination_centre_y + half_height);
const size_t mask_stride = ((size_t)source_width + 7U) / 8U;
const float source_centre_x = ((float)source_width - 1.0f) * 0.5f;
const float source_centre_y = ((float)source_height - 1.0f) * 0.5f;
for (int destination_y = start_y; destination_y <= end_y; ++destination_y) {
if (destination_y < 0 || destination_y >= LCD_HEIGHT) continue;
const float y = (float)(destination_y - destination_centre_y);
for (int destination_x = start_x; destination_x <= end_x; ++destination_x) {
if (destination_x < 0 || destination_x >= LCD_WIDTH) continue;
const float x = (float)(destination_x - destination_centre_x);
const int source_x = (int)floorf((cosine * x + sine * y) / scale +
source_centre_x + 0.5f);
const int source_y = (int)floorf((-sine * x + cosine * y) / scale +
source_centre_y + 0.5f);
if (source_x < 0 || source_x >= source_width ||
source_y < 0 || source_y >= source_height) continue;
if (mask != NULL &&
(mask[(size_t)source_y * mask_stride + (unsigned)source_x / 8U] &
(1U << (7U - (unsigned)source_x % 8U))) == 0) continue;
frame[(size_t)destination_y * LCD_WIDTH + destination_x] = pixels != NULL
? pixels[(size_t)source_y * source_width + source_x] : color;
}
}
}draw_bitmap_transformed() · source line 255
void draw_bitmap_transformed(uint16_t *frame, int destination_centre_x,
int destination_centre_y, const uint8_t *bitmap,
int source_width, int source_height,
float scale, float angle_degrees, uint16_t color)Calls into this documentation:
- Decision
bitmap == NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
draw_transformed_image(frame, destination_centre_x, destination_centre_y, NULL, bitmap, source_width, source_height, scale, angle_degrees, color);
Read original function source
void draw_bitmap_transformed(uint16_t *frame, int destination_centre_x,
int destination_centre_y, const uint8_t *bitmap,
int source_width, int source_height,
float scale, float angle_degrees, uint16_t color)
{
if (bitmap == NULL) return;
draw_transformed_image(frame, destination_centre_x, destination_centre_y,
NULL, bitmap, source_width, source_height,
scale, angle_degrees, color);
}draw_rgb565_transformed() · source line 266
void draw_rgb565_transformed(uint16_t *frame, int destination_centre_x,
int destination_centre_y, const uint16_t *pixels,
const uint8_t *mask, int source_width,
int source_height, float scale,
float angle_degrees)Calls into this documentation:
- Decision
pixels == NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
draw_transformed_image(frame, destination_centre_x, destination_centre_y, pixels, mask, source_width, source_height, scale, angle_degrees, 0);
Read original function source
void draw_rgb565_transformed(uint16_t *frame, int destination_centre_x,
int destination_centre_y, const uint16_t *pixels,
const uint8_t *mask, int source_width,
int source_height, float scale,
float angle_degrees)
{
if (pixels == NULL) return;
draw_transformed_image(frame, destination_centre_x, destination_centre_y,
pixels, mask, source_width, source_height,
scale, angle_degrees, 0);
}draw_ui_bitmap_8x8() · source line 278
void draw_ui_bitmap_8x8(uint16_t *frame, int x, int y,
const uint8_t bitmap[8], int scale, uint16_t color)Calls into this documentation:
- Decision
scale <= 0 || scale > INT_MAX / 8True- Exit this function
return;
FalseContinue after this decision.
- Execute
draw_bitmap_scaled(frame, x, y, bitmap, 8, 8, 8 * scale, 8 * scale, color);
Read original function source
void draw_ui_bitmap_8x8(uint16_t *frame, int x, int y,
const uint8_t bitmap[8], int scale, uint16_t color)
{
if (scale <= 0 || scale > INT_MAX / 8) {
return;
}
draw_bitmap_scaled(frame, x, y, bitmap, 8, 8, 8 * scale, 8 * scale, color);
}fill_circle() · source line 287
void fill_circle(uint16_t *frame, int x, int y, int radius,
uint16_t color)Calls into this documentation:
- FOR loop
int row = -radius; row <= radius; ++rowRepeat body while the loop condition permits; then continue below.
- FOR loop
int column = -radius; column <= radius; ++columnRepeat body while the loop condition permits; then continue below.
- Decision
column * column + row * row <= radius * radiusTrue- Execute
fill_rect(frame, x + column, y + row, 1, 1, color);
FalseContinue after this decision.
Read original function source
void fill_circle(uint16_t *frame, int x, int y, int radius,
uint16_t color)
{
for (int row = -radius; row <= radius; ++row) {
for (int column = -radius; column <= radius; ++column) {
if (column * column + row * row <= radius * radius) {
fill_rect(frame, x + column, y + row, 1, 1, color);
}
}
}
}ui_glyph() · source line 322
static const uint8_t *ui_glyph(char symbol)
- Execute
static const uint8_t colon[UI_FONT_HEIGHT] = {0,0x0C,0x0C,0,0x0C,0x0C,0,0}; - Execute
static const uint8_t slash[UI_FONT_HEIGHT] = {0x03,0x06,0x06,0x0C,0x18,0x18,0x30,0}; - Execute
static const uint8_t minus[UI_FONT_HEIGHT] = {0,0,0,0x3F,0,0,0,0}; - Execute
static const uint8_t plus[UI_FONT_HEIGHT] = {0,0x0C,0x0C,0x3F,0x0C,0x0C,0,0}; - Execute
static const uint8_t dot[UI_FONT_HEIGHT] = {0,0,0,0,0,0,0x0C,0}; - Execute
static const uint8_t apostrophe[UI_FONT_HEIGHT] = {0x0C,0x0C,0x18,0,0,0,0,0}; - Execute
static const uint8_t question[UI_FONT_HEIGHT] = {0x1E,0x33,0x03,0x06,0x0C,0,0x0C,0}; - Execute
static const uint8_t exclamation[UI_FONT_HEIGHT] = {0x0C,0x0C,0x0C,0x0C,0x0C,0,0x0C,0}; - Decision
symbol >= 'a' && symbol <= 'z'True- Execute
symbol = (char)(symbol - 'a' + 'A');
FalseContinue after this decision.
- Decision
symbol >= '0' && symbol <= '9'True- Exit this function
return ui_glyphs[symbol - '0'];
FalseContinue after this decision.
- Decision
symbol >= 'A' && symbol <= 'Z'True- Exit this function
return ui_glyphs[symbol - 'A' + 10];
FalseContinue after this decision.
- Select a case
symbolEnter matching case; follow source fall-through until a break, return or end.
- Case / label
case ':': - Exit this function
return colon; - Case / label
case '/': - Exit this function
return slash; - Case / label
case '-': - Exit this function
return minus; - Case / label
case '+': - Exit this function
return plus; - Case / label
case '.': - Exit this function
return dot; - Case / label
case '\'': - Exit this function
return apostrophe; - Case / label
case '?': - Exit this function
return question; - Case / label
case '!': - Exit this function
return exclamation; - Case / label
default: - Exit this function
return NULL;
Read original function source
static const uint8_t *ui_glyph(char symbol)
{
static const uint8_t colon[UI_FONT_HEIGHT] = {0,0x0C,0x0C,0,0x0C,0x0C,0,0};
static const uint8_t slash[UI_FONT_HEIGHT] = {0x03,0x06,0x06,0x0C,0x18,0x18,0x30,0};
static const uint8_t minus[UI_FONT_HEIGHT] = {0,0,0,0x3F,0,0,0,0};
static const uint8_t plus[UI_FONT_HEIGHT] = {0,0x0C,0x0C,0x3F,0x0C,0x0C,0,0};
static const uint8_t dot[UI_FONT_HEIGHT] = {0,0,0,0,0,0,0x0C,0};
static const uint8_t apostrophe[UI_FONT_HEIGHT] = {0x0C,0x0C,0x18,0,0,0,0,0};
static const uint8_t question[UI_FONT_HEIGHT] = {0x1E,0x33,0x03,0x06,0x0C,0,0x0C,0};
static const uint8_t exclamation[UI_FONT_HEIGHT] = {0x0C,0x0C,0x0C,0x0C,0x0C,0,0x0C,0};
if (symbol >= 'a' && symbol <= 'z') symbol = (char)(symbol - 'a' + 'A');
if (symbol >= '0' && symbol <= '9') return ui_glyphs[symbol - '0'];
if (symbol >= 'A' && symbol <= 'Z') return ui_glyphs[symbol - 'A' + 10];
switch (symbol) {
case ':': return colon;
case '/': return slash;
case '-': return minus;
case '+': return plus;
case '.': return dot;
case '\'': return apostrophe;
case '?': return question;
case '!': return exclamation;
default: return NULL;
}
}draw_text() · source line 348
void draw_text(uint16_t *frame, int x, int y, const char *text,
int scale, uint16_t color)Calls into this documentation:
- Decision
frame == NULL || text == NULL || scale <= 0True- Exit this function
return;
FalseContinue after this decision.
- FOR loop
; *text != '\0'; ++text, x += UI_FONT_ADVANCE * scaleRepeat body while the loop condition permits; then continue below.
- Execute
const uint8_t *glyph = ui_glyph(*text); - Decision
glyph == NULLTrue- Next loop iteration
continue;
FalseContinue after this decision.
- FOR loop
int row = 0; row < UI_FONT_HEIGHT; ++rowRepeat body while the loop condition permits; then continue below.
- FOR loop
int column = 0; column < UI_FONT_WIDTH; ++columnRepeat body while the loop condition permits; then continue below.
- Decision
(glyph[row] & (1U << (UI_FONT_WIDTH - 1 - column))) != 0True- Execute
fill_rect(frame, x + column * scale, y + row * scale, scale, scale, color);
FalseContinue after this decision.
Read original function source
void draw_text(uint16_t *frame, int x, int y, const char *text,
int scale, uint16_t color)
{
if (frame == NULL || text == NULL || scale <= 0) return;
for (; *text != '\0'; ++text, x += UI_FONT_ADVANCE * scale) {
const uint8_t *glyph = ui_glyph(*text);
if (glyph == NULL) continue;
for (int row = 0; row < UI_FONT_HEIGHT; ++row) {
for (int column = 0; column < UI_FONT_WIDTH; ++column) {
if ((glyph[row] & (1U << (UI_FONT_WIDTH - 1 - column))) != 0)
fill_rect(frame, x + column * scale, y + row * scale,
scale, scale, color);
}
}
}
}draw_rect() · source line 366
void draw_rect(uint16_t *frame, int x, int y, int width, int height,
uint16_t color)Calls into this documentation:
- Decision
width <= 0 || height <= 0True- Exit this function
return;
FalseContinue after this decision.
- Execute
fill_rect(frame, x, y, width, 1, color); - Execute
fill_rect(frame, x, y + height - 1, width, 1, color); - Execute
fill_rect(frame, x, y, 1, height, color); - Execute
fill_rect(frame, x + width - 1, y, 1, height, color);
Read original function source
void draw_rect(uint16_t *frame, int x, int y, int width, int height,
uint16_t color)
{
if (width <= 0 || height <= 0) {
return;
}
fill_rect(frame, x, y, width, 1, color);
fill_rect(frame, x, y + height - 1, width, 1, color);
fill_rect(frame, x, y, 1, height, color);
fill_rect(frame, x + width - 1, y, 1, height, color);
}draw_line() · source line 378
void draw_line(uint16_t *frame, int x0, int y0, int x1, int y1, uint16_t color)
Calls into this documentation:
- Execute
const int dx = abs(x1 - x0); - Execute
const int dy = -abs(y1 - y0); - Execute
const int sx = x0 < x1 ? 1 : -1; - Execute
const int sy = y0 < y1 ? 1 : -1; - Execute
int error = dx + dy; - WHILE loop
trueRepeat body while the loop condition permits; then continue below.
- Execute
fill_rect(frame, x0, y0, 1, 1, color); - Decision
x0 == x1 && y0 == y1True- Exit enclosing loop or switch
break;
FalseContinue after this decision.
- Execute
const int twice_error = 2 * error; - Decision
twice_error >= dyTrue- Execute
error += dy; - Execute
x0 += sx;
FalseContinue after this decision.
- Decision
twice_error <= dxTrue- Execute
error += dx; - Execute
y0 += sy;
FalseContinue after this decision.
Read original function source
void draw_line(uint16_t *frame, int x0, int y0, int x1, int y1, uint16_t color)
{
const int dx = abs(x1 - x0);
const int dy = -abs(y1 - y0);
const int sx = x0 < x1 ? 1 : -1;
const int sy = y0 < y1 ? 1 : -1;
int error = dx + dy;
while (true) {
fill_rect(frame, x0, y0, 1, 1, color);
if (x0 == x1 && y0 == y1) {
break;
}
const int twice_error = 2 * error;
if (twice_error >= dy) {
error += dy;
x0 += sx;
}
if (twice_error <= dx) {
error += dx;
y0 += sy;
}
}
}mix_dotted_seed() · source line 402
static uint32_t mix_dotted_seed(uint32_t value)
- Execute
value ^= value >> 16; - Execute
value *= 0x7FEB352DU; - Execute
value ^= value >> 15; - Execute
value *= 0x846CA68BU; - Exit this function
return value ^ (value >> 16);
Read original function source
static uint32_t mix_dotted_seed(uint32_t value)
{
value ^= value >> 16;
value *= 0x7FEB352DU;
value ^= value >> 15;
value *= 0x846CA68BU;
return value ^ (value >> 16);
}greatest_common_divisor() · source line 411
static unsigned greatest_common_divisor(unsigned left, unsigned right)
- WHILE loop
right != 0URepeat body while the loop condition permits; then continue below.
- Execute
const unsigned remainder = left % right; - Execute
left = right; - Execute
right = remainder;
- Exit this function
return left;
Read original function source
static unsigned greatest_common_divisor(unsigned left, unsigned right)
{
while (right != 0U) {
const unsigned remainder = left % right;
left = right;
right = remainder;
}
return left;
}dotted_index_is_active() · source line 421
static bool dotted_index_is_active(unsigned index, unsigned dot_count,
unsigned dot_density_percent, uint32_t random_seed)Calls into this documentation:
- Decision
dot_density_percent == 0U || dot_count == 0UTrue- Exit this function
return false;
FalseContinue after this decision.
- Decision
dot_density_percent >= 100U || dot_count == 1UTrue- Exit this function
return true;
FalseContinue after this decision.
- Execute
const unsigned active_count = (dot_count * dot_density_percent + 99U) / 100U; - Decision
active_count >= dot_countTrue- Exit this function
return true;
FalseContinue after this decision.
- Execute
const unsigned start = mix_dotted_seed(random_seed) % dot_count; - Execute
unsigned step = 1U + mix_dotted_seed(random_seed ^ 0x9E3779B9U) % (dot_count - 1U); - WHILE loop
greatest_common_divisor(step, dot_count) != 1URepeat body while the loop condition permits; then continue below.
- Execute
++step; - Decision
step >= dot_countTrue- Execute
step = 1U;
FalseContinue after this decision.
- FOR loop
unsigned selected = 0U; selected < active_count; ++selectedRepeat body while the loop condition permits; then continue below.
- Execute
const unsigned candidate = (unsigned)(((uint64_t)start + (uint64_t)selected * step) % dot_count); - Decision
candidate == indexTrue- Exit this function
return true;
FalseContinue after this decision.
- Exit this function
return false;
Read original function source
static bool dotted_index_is_active(unsigned index, unsigned dot_count,
unsigned dot_density_percent, uint32_t random_seed)
{
if (dot_density_percent == 0U || dot_count == 0U) return false;
if (dot_density_percent >= 100U || dot_count == 1U) return true;
const unsigned active_count = (dot_count * dot_density_percent + 99U) / 100U;
if (active_count >= dot_count) return true;
const unsigned start = mix_dotted_seed(random_seed) % dot_count;
unsigned step = 1U + mix_dotted_seed(random_seed ^ 0x9E3779B9U) % (dot_count - 1U);
while (greatest_common_divisor(step, dot_count) != 1U) {
++step;
if (step >= dot_count) step = 1U;
}
for (unsigned selected = 0U; selected < active_count; ++selected) {
const unsigned candidate = (unsigned)(((uint64_t)start +
(uint64_t)selected * step) % dot_count);
if (candidate == index) return true;
}
return false;
}dotted_index_is_sparkle() · source line 444
static bool dotted_index_is_sparkle(unsigned index, unsigned sparkle_density_percent,
uint32_t random_seed)Calls into this documentation:
- Decision
sparkle_density_percent == 0UTrue- Exit this function
return false;
FalseContinue after this decision.
- Decision
sparkle_density_percent >= 100UTrue- Exit this function
return true;
FalseContinue after this decision.
- Execute
const uint32_t value = mix_dotted_seed(random_seed ^ index * 0x9E3779B9U); - Exit this function
return value % 100U < sparkle_density_percent;
Read original function source
static bool dotted_index_is_sparkle(unsigned index, unsigned sparkle_density_percent,
uint32_t random_seed)
{
if (sparkle_density_percent == 0U) return false;
if (sparkle_density_percent >= 100U) return true;
const uint32_t value = mix_dotted_seed(random_seed ^ index * 0x9E3779B9U);
return value % 100U < sparkle_density_percent;
}draw_dotted_line_density() · source line 453
void draw_dotted_line_density(uint16_t *frame, int x0, int y0, int x1, int y1,
unsigned dot_spacing, int dot_radius, uint16_t color,
unsigned dot_density_percent, uint32_t random_seed)Calls into this documentation:
- Decision
frame == NULL || dot_spacing == 0U || dot_radius < 0 || dot_density_percent > 100UTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
const float delta_x = (float)x1 - (float)x0; - Execute
const float delta_y = (float)y1 - (float)y0; - Execute
const float line_length = sqrtf(delta_x * delta_x + delta_y * delta_y); - Decision
line_length == 0.0fTrue- Decision
dotted_index_is_active(0U, 1U, dot_density_percent, random_seed)True- Execute
fill_circle(frame, x0, y0, dot_radius, color);
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Execute
const unsigned dot_count = (unsigned)floorf(line_length / (float)dot_spacing) + 1U; - FOR loop
unsigned index = 0U; index < dot_count; ++indexRepeat body while the loop condition permits; then continue below.
- Decision
!dotted_index_is_active(index, dot_count, dot_density_percent, random_seed)True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
const float distance = (float)index * (float)dot_spacing; - Execute
const float progress = distance / line_length; - Execute
const int x = x0 + (int)lroundf(delta_x * progress); - Execute
const int y = y0 + (int)lroundf(delta_y * progress); - Execute
fill_circle(frame, x, y, dot_radius, color);
Read original function source
void draw_dotted_line_density(uint16_t *frame, int x0, int y0, int x1, int y1,
unsigned dot_spacing, int dot_radius, uint16_t color,
unsigned dot_density_percent, uint32_t random_seed)
{
if (frame == NULL || dot_spacing == 0U || dot_radius < 0 ||
dot_density_percent > 100U) return;
const float delta_x = (float)x1 - (float)x0;
const float delta_y = (float)y1 - (float)y0;
const float line_length = sqrtf(delta_x * delta_x + delta_y * delta_y);
if (line_length == 0.0f) {
if (dotted_index_is_active(0U, 1U, dot_density_percent, random_seed)) {
fill_circle(frame, x0, y0, dot_radius, color);
}
return;
}
const unsigned dot_count = (unsigned)floorf(line_length / (float)dot_spacing) + 1U;
for (unsigned index = 0U; index < dot_count; ++index) {
if (!dotted_index_is_active(index, dot_count, dot_density_percent, random_seed)) {
continue;
}
const float distance = (float)index * (float)dot_spacing;
const float progress = distance / line_length;
const int x = x0 + (int)lroundf(delta_x * progress);
const int y = y0 + (int)lroundf(delta_y * progress);
fill_circle(frame, x, y, dot_radius, color);
}
}draw_dotted_line() · source line 481
void draw_dotted_line(uint16_t *frame, int x0, int y0, int x1, int y1,
unsigned dot_spacing, int dot_radius, uint16_t color)Calls into this documentation:
- Execute
draw_dotted_line_density(frame, x0, y0, x1, y1, dot_spacing, dot_radius, color, 100U, 0U);
Read original function source
void draw_dotted_line(uint16_t *frame, int x0, int y0, int x1, int y1,
unsigned dot_spacing, int dot_radius, uint16_t color)
{
draw_dotted_line_density(frame, x0, y0, x1, y1, dot_spacing, dot_radius, color,
100U, 0U);
}packed_bitmap_pixel() · source line 488
static bool packed_bitmap_pixel(const uint8_t *bitmap, unsigned source_width,
unsigned x, unsigned y)- Execute
const size_t row_bytes = ((size_t)source_width + 7U) / 8U; - Execute
const size_t byte_index = (size_t)y * row_bytes + x / 8U; - Exit this function
return (bitmap[byte_index] & (uint8_t)(1U << (7U - x % 8U))) != 0U;
Read original function source
static bool packed_bitmap_pixel(const uint8_t *bitmap, unsigned source_width,
unsigned x, unsigned y)
{
const size_t row_bytes = ((size_t)source_width + 7U) / 8U;
const size_t byte_index = (size_t)y * row_bytes + x / 8U;
return (bitmap[byte_index] & (uint8_t)(1U << (7U - x % 8U))) != 0U;
}project_dotted_bitmap_point() · source line 505
static graphics_point_t project_dotted_bitmap_point(float source_x, float source_y,
float depth,
const dotted_bitmap_3d_params_t *params,
const dotted_bitmap_rotation_t *rotation)- Execute
const float scaled_x = source_x * params->scale; - Execute
const float scaled_y = source_y * params->scale; - Execute
const float pitched_y = scaled_y * rotation->pitch_cosine - depth * rotation->pitch_sine; - Execute
const float pitched_z = scaled_y * rotation->pitch_sine + depth * rotation->pitch_cosine; - Execute
const float yawed_x = scaled_x * rotation->yaw_cosine + pitched_z * rotation->yaw_sine; - Execute
const float yawed_z = -scaled_x * rotation->yaw_sine + pitched_z * rotation->yaw_cosine; - Execute
const float rotated_x = yawed_x * rotation->roll_cosine - pitched_y * rotation->roll_sine; - Execute
const float rotated_y = yawed_x * rotation->roll_sine + pitched_y * rotation->roll_cosine; - Execute
const float perspective = params->camera_distance / (params->camera_distance + yawed_z); - Execute
const graphics_point_t point = { .x = params->centre_x + (int)lroundf(rotated_x * perspective), .y = params->centre_y + (int)lroundf(rotated_y * perspective), }; - Exit this function
return point;
Read original function source
static graphics_point_t project_dotted_bitmap_point(float source_x, float source_y,
float depth,
const dotted_bitmap_3d_params_t *params,
const dotted_bitmap_rotation_t *rotation)
{
const float scaled_x = source_x * params->scale;
const float scaled_y = source_y * params->scale;
const float pitched_y = scaled_y * rotation->pitch_cosine -
depth * rotation->pitch_sine;
const float pitched_z = scaled_y * rotation->pitch_sine +
depth * rotation->pitch_cosine;
const float yawed_x = scaled_x * rotation->yaw_cosine +
pitched_z * rotation->yaw_sine;
const float yawed_z = -scaled_x * rotation->yaw_sine +
pitched_z * rotation->yaw_cosine;
const float rotated_x = yawed_x * rotation->roll_cosine -
pitched_y * rotation->roll_sine;
const float rotated_y = yawed_x * rotation->roll_sine +
pitched_y * rotation->roll_cosine;
const float perspective = params->camera_distance /
(params->camera_distance + yawed_z);
const graphics_point_t point = {
.x = params->centre_x + (int)lroundf(rotated_x * perspective),
.y = params->centre_y + (int)lroundf(rotated_y * perspective),
};
return point;
}draw_dotted_bitmap_depth_line() · source line 533
static void draw_dotted_bitmap_depth_line(uint16_t *frame,
float x, float y,
const dotted_bitmap_3d_params_t *params,
const dotted_bitmap_rotation_t *rotation,
uint32_t random_seed)Calls into this documentation:
- Execute
const graphics_point_t front = project_dotted_bitmap_point(x, y, 0.0f, params, rotation); - Execute
const graphics_point_t back = project_dotted_bitmap_point(x, y, params->depth, params, rotation); - Execute
draw_dotted_line_density(frame, front.x, front.y, back.x, back.y, params->depth_dot_spacing, params->dot_radius, params->side_color, params->dot_density_percent, random_seed);
Read original function source
static void draw_dotted_bitmap_depth_line(uint16_t *frame,
float x, float y,
const dotted_bitmap_3d_params_t *params,
const dotted_bitmap_rotation_t *rotation,
uint32_t random_seed)
{
const graphics_point_t front = project_dotted_bitmap_point(x, y, 0.0f,
params, rotation);
const graphics_point_t back = project_dotted_bitmap_point(x, y, params->depth,
params, rotation);
draw_dotted_line_density(frame, front.x, front.y, back.x, back.y,
params->depth_dot_spacing, params->dot_radius,
params->side_color, params->dot_density_percent,
random_seed);
}draw_dotted_bitmap_face() · source line 549
static void draw_dotted_bitmap_face(uint16_t *frame, const uint8_t *bitmap,
unsigned source_width, unsigned source_height,
float depth, uint16_t color,
const dotted_bitmap_3d_params_t *params,
const dotted_bitmap_rotation_t *rotation,
uint32_t random_seed,
bool draw_sparkles)Calls into this documentation:
- Execute
const float half_width = (float)source_width * 0.5f; - Execute
const float half_height = (float)source_height * 0.5f; - FOR loop
unsigned y = 0U; y < source_height; ++yRepeat body while the loop condition permits; then continue below.
- Execute
unsigned run_start = 0U; - WHILE loop
run_start < source_widthRepeat body while the loop condition permits; then continue below.
- WHILE loop
run_start < source_width && !packed_bitmap_pixel(bitmap, source_width, run_start, y)Repeat body while the loop condition permits; then continue below.
- Execute
++run_start;
- Decision
run_start == source_widthTrue- Exit enclosing loop or switch
break;
FalseContinue after this decision.
- Execute
unsigned run_end = run_start + 1U; - WHILE loop
run_end < source_width && packed_bitmap_pixel(bitmap, source_width, run_end, y)Repeat body while the loop condition permits; then continue below.
- Execute
++run_end;
- Execute
const unsigned dot_count = run_end - run_start; - Execute
const uint32_t line_seed = random_seed ^ y * 0x45D9F3BU ^ run_start * 0x119DE1F3U; - FOR loop
unsigned index = 0U; index < dot_count; ++indexRepeat body while the loop condition permits; then continue below.
- Decision
!dotted_index_is_active(index, dot_count, params->dot_density_percent, line_seed)True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
const float source_x = (float)(run_start + index) + 0.5f - half_width; - Execute
const float source_y = (float)y + 0.5f - half_height; - Execute
const graphics_point_t dot = project_dotted_bitmap_point(source_x, source_y, depth, params, rotation); - Execute
fill_circle(frame, dot.x, dot.y, params->dot_radius, color); - Decision
draw_sparkles && params->sparkle_density_percent != 0U && dotted_index_is_sparkle(index, params->sparkle_density_percent, line_seed ^ params->sparkle_phase * 0x27D4EB2DU)True- Execute
fill_circle(frame, dot.x, dot.y, params->sparkle_radius, params->sparkle_color);
FalseContinue after this decision.
- Execute
run_start = run_end;
Read original function source
static void draw_dotted_bitmap_face(uint16_t *frame, const uint8_t *bitmap,
unsigned source_width, unsigned source_height,
float depth, uint16_t color,
const dotted_bitmap_3d_params_t *params,
const dotted_bitmap_rotation_t *rotation,
uint32_t random_seed,
bool draw_sparkles)
{
const float half_width = (float)source_width * 0.5f;
const float half_height = (float)source_height * 0.5f;
for (unsigned y = 0U; y < source_height; ++y) {
unsigned run_start = 0U;
while (run_start < source_width) {
while (run_start < source_width &&
!packed_bitmap_pixel(bitmap, source_width, run_start, y)) {
++run_start;
}
if (run_start == source_width) break;
unsigned run_end = run_start + 1U;
while (run_end < source_width &&
packed_bitmap_pixel(bitmap, source_width, run_end, y)) {
++run_end;
}
const unsigned dot_count = run_end - run_start;
const uint32_t line_seed = random_seed ^ y * 0x45D9F3BU ^
run_start * 0x119DE1F3U;
for (unsigned index = 0U; index < dot_count; ++index) {
if (!dotted_index_is_active(index, dot_count,
params->dot_density_percent, line_seed)) {
continue;
}
const float source_x = (float)(run_start + index) + 0.5f - half_width;
const float source_y = (float)y + 0.5f - half_height;
const graphics_point_t dot = project_dotted_bitmap_point(source_x, source_y,
depth, params,
rotation);
fill_circle(frame, dot.x, dot.y, params->dot_radius, color);
if (draw_sparkles && params->sparkle_density_percent != 0U &&
dotted_index_is_sparkle(index, params->sparkle_density_percent,
line_seed ^ params->sparkle_phase * 0x27D4EB2DU)) {
fill_circle(frame, dot.x, dot.y, params->sparkle_radius,
params->sparkle_color);
}
}
run_start = run_end;
}
}
}draw_bitmap_dotted_3d() · source line 598
void draw_bitmap_dotted_3d(uint16_t *frame, const uint8_t *bitmap,
unsigned source_width, unsigned source_height,
const dotted_bitmap_3d_params_t *params)Calls into this documentation:
- Decision
frame == NULL || bitmap == NULL || params == NULL || source_width == 0U || source_height == 0U || params->scale <= 0.0f || params->depth <= 0.0f || params->camera_distance <= 0.0f || params->depth_dot_spacing == 0U || params->dot_radius < 0 || params->dot_density_percent > 100U || params->sparkle_density_percent > 100U || params->sparkle_radius < 0 || !isfinite(params->scale) || !isfinite(params->pitch_degrees) || !isfinite(params->yaw_degrees) || !isfinite(params->roll_degrees) || !isfinite(params->depth) || !isfinite(params->camera_distance)True- Exit this function
return;
FalseContinue after this decision.
- Execute
const float half_width = (float)source_width * params->scale * 0.5f; - Execute
const float half_height = (float)source_height * params->scale * 0.5f; - Execute
const float safe_camera_distance = sqrtf(half_width * half_width + half_height * half_height + params->depth * params->depth); - Decision
params->camera_distance <= safe_camera_distanceTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
const float pitch_radians = params->pitch_degrees * 0.01745329252f; - Execute
const float yaw_radians = params->yaw_degrees * 0.01745329252f; - Execute
const float roll_radians = params->roll_degrees * 0.01745329252f; - Execute
const dotted_bitmap_rotation_t rotation = { .pitch_cosine = cosf(pitch_radians), .pitch_sine = sinf(pitch_radians), .yaw_cosine = cosf(yaw_radians), .yaw_sine = sinf(yaw_radians), .roll_cosine = cosf(roll_radians), .roll_sine = sinf(roll_radians), }; - Execute
const float source_half_width = (float)source_width * 0.5f; - Execute
const float source_half_height = (float)source_height * 0.5f; - Execute
draw_dotted_bitmap_face(frame, bitmap, source_width, source_height, params->depth, params->back_color, params, &rotation, params->random_seed ^ 0xA511E9B3U, false); - FOR loop
unsigned y = 0U; y < source_height; ++yRepeat body while the loop condition permits; then continue below.
- Execute
unsigned run_start = 0U; - WHILE loop
run_start < source_widthRepeat body while the loop condition permits; then continue below.
- WHILE loop
run_start < source_width && !packed_bitmap_pixel(bitmap, source_width, run_start, y)Repeat body while the loop condition permits; then continue below.
- Execute
++run_start;
- Decision
run_start == source_widthTrue- Exit enclosing loop or switch
break;
FalseContinue after this decision.
- Execute
unsigned run_end = run_start + 1U; - WHILE loop
run_end < source_width && packed_bitmap_pixel(bitmap, source_width, run_end, y)Repeat body while the loop condition permits; then continue below.
- Execute
++run_end;
- Execute
const float edge_y = (float)y - source_half_height; - Execute
draw_dotted_bitmap_depth_line(frame, (float)run_start - source_half_width, edge_y, params, &rotation, params->random_seed ^ y * 0x45D9F3BU ^ run_start * 0x119DE1F3U ^ 0x3C6EF372U); - Execute
draw_dotted_bitmap_depth_line(frame, (float)run_end - source_half_width, edge_y, params, &rotation, params->random_seed ^ y * 0x45D9F3BU ^ run_end * 0x119DE1F3U ^ 0xDAA66D2BU); - Execute
run_start = run_end;
- Execute
draw_dotted_bitmap_face(frame, bitmap, source_width, source_height, 0.0f, params->front_color, params, &rotation, params->random_seed ^ 0x63D83595U, true);
Read original function source
void draw_bitmap_dotted_3d(uint16_t *frame, const uint8_t *bitmap,
unsigned source_width, unsigned source_height,
const dotted_bitmap_3d_params_t *params)
{
if (frame == NULL || bitmap == NULL || params == NULL || source_width == 0U ||
source_height == 0U || params->scale <= 0.0f || params->depth <= 0.0f ||
params->camera_distance <= 0.0f || params->depth_dot_spacing == 0U ||
params->dot_radius < 0 || params->dot_density_percent > 100U ||
params->sparkle_density_percent > 100U || params->sparkle_radius < 0 ||
!isfinite(params->scale) || !isfinite(params->pitch_degrees) ||
!isfinite(params->yaw_degrees) || !isfinite(params->roll_degrees) ||
!isfinite(params->depth) ||
!isfinite(params->camera_distance)) {
return;
}
const float half_width = (float)source_width * params->scale * 0.5f;
const float half_height = (float)source_height * params->scale * 0.5f;
const float safe_camera_distance = sqrtf(half_width * half_width +
half_height * half_height +
params->depth * params->depth);
if (params->camera_distance <= safe_camera_distance) {
return;
}
const float pitch_radians = params->pitch_degrees * 0.01745329252f;
const float yaw_radians = params->yaw_degrees * 0.01745329252f;
const float roll_radians = params->roll_degrees * 0.01745329252f;
const dotted_bitmap_rotation_t rotation = {
.pitch_cosine = cosf(pitch_radians),
.pitch_sine = sinf(pitch_radians),
.yaw_cosine = cosf(yaw_radians),
.yaw_sine = sinf(yaw_radians),
.roll_cosine = cosf(roll_radians),
.roll_sine = sinf(roll_radians),
};
const float source_half_width = (float)source_width * 0.5f;
const float source_half_height = (float)source_height * 0.5f;
// Draw the back first, then edge joins, then front, so the transparent
// dotted volume remains readable over the caller's existing background.
draw_dotted_bitmap_face(frame, bitmap, source_width, source_height,
params->depth, params->back_color, params, &rotation,
params->random_seed ^ 0xA511E9B3U, false);
for (unsigned y = 0U; y < source_height; ++y) {
unsigned run_start = 0U;
while (run_start < source_width) {
while (run_start < source_width &&
!packed_bitmap_pixel(bitmap, source_width, run_start, y)) {
++run_start;
}
if (run_start == source_width) break;
unsigned run_end = run_start + 1U;
while (run_end < source_width &&
packed_bitmap_pixel(bitmap, source_width, run_end, y)) {
++run_end;
}
const float edge_y = (float)y - source_half_height;
draw_dotted_bitmap_depth_line(frame, (float)run_start - source_half_width, edge_y,
params, &rotation,
params->random_seed ^ y * 0x45D9F3BU ^
run_start * 0x119DE1F3U ^ 0x3C6EF372U);
draw_dotted_bitmap_depth_line(frame, (float)run_end - source_half_width, edge_y,
params, &rotation,
params->random_seed ^ y * 0x45D9F3BU ^
run_end * 0x119DE1F3U ^ 0xDAA66D2BU);
run_start = run_end;
}
}
draw_dotted_bitmap_face(frame, bitmap, source_width, source_height,
0.0f, params->front_color, params, &rotation,
params->random_seed ^ 0x63D83595U, true);
}fill_convex_polygon() · source line 674
void fill_convex_polygon(uint16_t *frame, const graphics_point_t *points,
size_t point_count, uint16_t color)Calls into this documentation:
- Decision
frame == NULL || points == NULL || point_count < 3UTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
int minimum_y = points[0].y; - Execute
int maximum_y = points[0].y; - FOR loop
size_t point = 1U; point < point_count; ++pointRepeat body while the loop condition permits; then continue below.
- Decision
points[point].y < minimum_yTrue- Execute
minimum_y = points[point].y;
FalseContinue after this decision.
- Decision
points[point].y > maximum_yTrue- Execute
maximum_y = points[point].y;
FalseContinue after this decision.
- Decision
minimum_y < 0True- Execute
minimum_y = 0;
FalseContinue after this decision.
- Decision
maximum_y >= LCD_HEIGHTTrue- Execute
maximum_y = LCD_HEIGHT - 1;
FalseContinue after this decision.
- FOR loop
int y = minimum_y; y <= maximum_y; ++yRepeat body while the loop condition permits; then continue below.
- Execute
int left = LCD_WIDTH; - Execute
int right = -1; - FOR loop
size_t point = 0U; point < point_count; ++pointRepeat body while the loop condition permits; then continue below.
- Execute
const graphics_point_t first = points[point]; - Execute
const graphics_point_t second = points[(point + 1U) % point_count]; - Execute
const int edge_min_y = first.y < second.y ? first.y : second.y; - Execute
const int edge_max_y = first.y > second.y ? first.y : second.y; - Decision
edge_min_y == edge_max_y || y < edge_min_y || y >= edge_max_yTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
const int x = first.x + (y - first.y) * (second.x - first.x) / (second.y - first.y); - Decision
x < leftTrue- Execute
left = x;
FalseContinue after this decision.
- Decision
x > rightTrue- Execute
right = x;
FalseContinue after this decision.
- Decision
right < 0 || left >= LCD_WIDTHTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Decision
left < 0True- Execute
left = 0;
FalseContinue after this decision.
- Decision
right >= LCD_WIDTHTrue- Execute
right = LCD_WIDTH - 1;
FalseContinue after this decision.
- Decision
left <= rightTrue- Execute
fill_rect(frame, left, y, right - left + 1, 1, color);
FalseContinue after this decision.
Read original function source
void fill_convex_polygon(uint16_t *frame, const graphics_point_t *points,
size_t point_count, uint16_t color)
{
if (frame == NULL || points == NULL || point_count < 3U) return;
int minimum_y = points[0].y;
int maximum_y = points[0].y;
for (size_t point = 1U; point < point_count; ++point) {
if (points[point].y < minimum_y) minimum_y = points[point].y;
if (points[point].y > maximum_y) maximum_y = points[point].y;
}
if (minimum_y < 0) minimum_y = 0;
if (maximum_y >= LCD_HEIGHT) maximum_y = LCD_HEIGHT - 1;
for (int y = minimum_y; y <= maximum_y; ++y) {
int left = LCD_WIDTH;
int right = -1;
for (size_t point = 0U; point < point_count; ++point) {
const graphics_point_t first = points[point];
const graphics_point_t second = points[(point + 1U) % point_count];
const int edge_min_y = first.y < second.y ? first.y : second.y;
const int edge_max_y = first.y > second.y ? first.y : second.y;
if (edge_min_y == edge_max_y || y < edge_min_y || y >= edge_max_y) continue;
const int x = first.x + (y - first.y) * (second.x - first.x) /
(second.y - first.y);
if (x < left) left = x;
if (x > right) right = x;
}
if (right < 0 || left >= LCD_WIDTH) continue;
if (left < 0) left = 0;
if (right >= LCD_WIDTH) right = LCD_WIDTH - 1;
if (left <= right) fill_rect(frame, left, y, right - left + 1, 1, color);
}
}draw_ring() · source line 709
void draw_ring(uint16_t *frame, int x, int y, int radius, uint16_t color)
Calls into this documentation:
- FOR loop
int row = -radius; row <= radius; ++rowRepeat body while the loop condition permits; then continue below.
- FOR loop
int column = -radius; column <= radius; ++columnRepeat body while the loop condition permits; then continue below.
- Execute
const int distance = column * column + row * row; - Decision
distance <= radius * radius && (radius == 0 || distance > (radius - 1) * (radius - 1))True- Execute
fill_rect(frame, x + column, y + row, 1, 1, color);
FalseContinue after this decision.
Read original function source
void draw_ring(uint16_t *frame, int x, int y, int radius, uint16_t color)
{
for (int row = -radius; row <= radius; ++row) {
for (int column = -radius; column <= radius; ++column) {
const int distance = column * column + row * row;
if (distance <= radius * radius &&
(radius == 0 || distance > (radius - 1) * (radius - 1))) {
fill_rect(frame, x + column, y + row, 1, 1, color);
}
}
}
}draw_text_centred() · source line 722
void draw_text_centred(uint16_t *frame, int x, int y, const char *text,
int scale, uint16_t color)Calls into this documentation:
- Execute
const int length = (int)strlen(text); - Execute
const int width = length > 0 ? length * UI_FONT_ADVANCE * scale - scale : 0; - Execute
draw_text(frame, x - width / 2, y, text, scale, color);
Read original function source
void draw_text_centred(uint16_t *frame, int x, int y, const char *text,
int scale, uint16_t color)
{
const int length = (int)strlen(text);
const int width = length > 0 ? length * UI_FONT_ADVANCE * scale - scale : 0;
draw_text(frame, x - width / 2, y, text, scale, color);
}draw_text_transformed() · source line 730
void draw_text_transformed(uint16_t *frame, int destination_centre_x,
int destination_centre_y, const char *text,
int source_scale, float transform_scale,
float angle_degrees, uint16_t color)Calls into this documentation:
- Decision
frame == NULL || text == NULL || source_scale <= 0 || transform_scale <= 0.0f || !isfinite(transform_scale) || !isfinite(angle_degrees)True- Exit this function
return;
FalseContinue after this decision.
- Execute
int length = 0; - WHILE loop
text[length] != '\0'Repeat body while the loop condition permits; then continue below.
- Execute
++length;
- Decision
length == 0True- Exit this function
return;
FalseContinue after this decision.
- Execute
const int source_width = length * UI_FONT_ADVANCE * source_scale - source_scale; - Execute
const int source_height = UI_FONT_HEIGHT * source_scale; - Execute
const float radians = angle_degrees * 0.01745329252f; - Execute
const float cosine = cosf(radians), sine = sinf(radians); - Execute
const float centre_x = ((float)source_width - 1.0f) * 0.5f; - Execute
const float centre_y = ((float)source_height - 1.0f) * 0.5f; - Execute
int glyph_x = 0; - FOR loop
int character = 0; character < length; ++character, glyph_x += UI_FONT_ADVANCE * source_scaleRepeat body while the loop condition permits; then continue below.
- Execute
const uint8_t *glyph = ui_glyph(text[character]); - Decision
glyph == NULLTrue- Next loop iteration
continue;
FalseContinue after this decision.
- FOR loop
int row = 0; row < UI_FONT_HEIGHT; ++rowRepeat body while the loop condition permits; then continue below.
- FOR loop
int column = 0; column < UI_FONT_WIDTH; ++columnRepeat body while the loop condition permits; then continue below.
- Decision
(glyph[row] & (1U << (UI_FONT_WIDTH - 1 - column))) == 0True- Next loop iteration
continue;
FalseContinue after this decision.
- FOR loop
int pixel_y = 0; pixel_y < source_scale; ++pixel_yRepeat body while the loop condition permits; then continue below.
- FOR loop
int pixel_x = 0; pixel_x < source_scale; ++pixel_xRepeat body while the loop condition permits; then continue below.
- Execute
const float source_x = glyph_x + column * source_scale + pixel_x - centre_x; - Execute
const float source_y = row * source_scale + pixel_y - centre_y; - Execute
const int destination_x = (int)lroundf(destination_centre_x + (cosine * source_x - sine * source_y) * transform_scale); - Execute
const int destination_y = (int)lroundf(destination_centre_y + (sine * source_x + cosine * source_y) * transform_scale); - Decision
destination_x >= 0 && destination_x < LCD_WIDTH && destination_y >= 0 && destination_y < LCD_HEIGHTTrue- Execute
frame[(size_t)destination_y * LCD_WIDTH + destination_x] = color;
FalseContinue after this decision.
Read original function source
void draw_text_transformed(uint16_t *frame, int destination_centre_x,
int destination_centre_y, const char *text,
int source_scale, float transform_scale,
float angle_degrees, uint16_t color)
{
if (frame == NULL || text == NULL || source_scale <= 0 ||
transform_scale <= 0.0f || !isfinite(transform_scale) ||
!isfinite(angle_degrees)) return;
int length = 0;
while (text[length] != '\0') ++length;
if (length == 0) return;
const int source_width = length * UI_FONT_ADVANCE * source_scale - source_scale;
const int source_height = UI_FONT_HEIGHT * source_scale;
const float radians = angle_degrees * 0.01745329252f;
const float cosine = cosf(radians), sine = sinf(radians);
const float centre_x = ((float)source_width - 1.0f) * 0.5f;
const float centre_y = ((float)source_height - 1.0f) * 0.5f;
int glyph_x = 0;
for (int character = 0; character < length; ++character, glyph_x += UI_FONT_ADVANCE * source_scale) {
const uint8_t *glyph = ui_glyph(text[character]);
if (glyph == NULL) continue;
for (int row = 0; row < UI_FONT_HEIGHT; ++row) for (int column = 0; column < UI_FONT_WIDTH; ++column) {
if ((glyph[row] & (1U << (UI_FONT_WIDTH - 1 - column))) == 0) continue;
for (int pixel_y = 0; pixel_y < source_scale; ++pixel_y)
for (int pixel_x = 0; pixel_x < source_scale; ++pixel_x) {
const float source_x = glyph_x + column * source_scale + pixel_x - centre_x;
const float source_y = row * source_scale + pixel_y - centre_y;
const int destination_x = (int)lroundf(destination_centre_x +
(cosine * source_x - sine * source_y) * transform_scale);
const int destination_y = (int)lroundf(destination_centre_y +
(sine * source_x + cosine * source_y) * transform_scale);
if (destination_x >= 0 && destination_x < LCD_WIDTH &&
destination_y >= 0 && destination_y < LCD_HEIGHT)
frame[(size_t)destination_y * LCD_WIDTH + destination_x] = color;
}
}
}
}radial_flame_random() · source line 772
static uint32_t radial_flame_random(radial_flame_t *flame)
- Execute
flame->random_state ^= flame->random_state << 13; - Execute
flame->random_state ^= flame->random_state >> 17; - Execute
flame->random_state ^= flame->random_state << 5; - Exit this function
return flame->random_state;
Read original function source
static uint32_t radial_flame_random(radial_flame_t *flame)
{
flame->random_state ^= flame->random_state << 13;
flame->random_state ^= flame->random_state >> 17;
flame->random_state ^= flame->random_state << 5;
return flame->random_state;
}radial_flame_initialize() · source line 780
void radial_flame_initialize(radial_flame_t *flame, uint8_t *heat,
unsigned segments, unsigned layers,
float layer_depth, uint32_t seed)- Decision
flame == NULL || heat == NULL || segments < 2U || layers < 2U || layer_depth <= 0.0f || !isfinite(layer_depth)True- Exit this function
return;
FalseContinue after this decision.
- Execute
flame->heat = heat; - Execute
flame->segments = segments; - Execute
flame->layers = layers; - Execute
flame->layer_depth = layer_depth; - Execute
flame->random_state = seed == 0U ? 0x6D2B79F5U : seed; - Execute
memset(heat, 0, (size_t)segments * layers);
Read original function source
void radial_flame_initialize(radial_flame_t *flame, uint8_t *heat,
unsigned segments, unsigned layers,
float layer_depth, uint32_t seed)
{
if (flame == NULL || heat == NULL || segments < 2U || layers < 2U ||
layer_depth <= 0.0f || !isfinite(layer_depth)) return;
flame->heat = heat;
flame->segments = segments;
flame->layers = layers;
flame->layer_depth = layer_depth;
flame->random_state = seed == 0U ? 0x6D2B79F5U : seed;
memset(heat, 0, (size_t)segments * layers);
}radial_flame_update_internal() · source line 794
static void radial_flame_update_internal(radial_flame_t *flame, float wave_amplitude,
float wave_count, float wave_phase_degrees,
unsigned extra_cooling)Calls into this documentation:
- Decision
flame == NULL || flame->heat == NULL || flame->segments < 2U || flame->layers < 2UTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
extra_cooling > 248UTrue- Execute
extra_cooling = 248U;
FalseContinue after this decision.
- FOR loop
unsigned layer = flame->layers - 1U; layer > 0U; --layerRepeat body while the loop condition permits; then continue below.
- FOR loop
unsigned segment = 0U; segment < flame->segments; ++segmentRepeat body while the loop condition permits; then continue below.
- Execute
int source = (int)segment + (int)(radial_flame_random(flame) % 3U) - 1; - Decision
source < 0True- Execute
source += (int)flame->segments;
FalseContinue after this decision.
- Decision
source >= (int)flame->segmentsTrue- Execute
source -= (int)flame->segments;
FalseContinue after this decision.
- Execute
const int heat = flame->heat[(layer - 1U) * flame->segments + (unsigned)source] - (int)(3U + radial_flame_random(flame) % (7U + extra_cooling)); - Execute
flame->heat[layer * flame->segments + segment] = heat > 0 ? (uint8_t)heat : 0U;
- Execute
const float phase = wave_phase_degrees * 0.01745329252f; - FOR loop
unsigned segment = 0U; segment < flame->segments; ++segmentRepeat body while the loop condition permits; then continue below.
- Execute
const float position = (float)segment * 6.28318531f * wave_count / flame->segments; - Execute
int heat = 170 + (int)(radial_flame_random(flame) % 86U) + (int)lroundf(sinf(position + phase) * wave_amplitude); - Decision
heat < 40True- Execute
heat = 40;
FalseContinue after this decision.
- Decision
heat > 255True- Execute
heat = 255;
FalseContinue after this decision.
- Execute
flame->heat[segment] = (uint8_t)heat;
Read original function source
static void radial_flame_update_internal(radial_flame_t *flame, float wave_amplitude,
float wave_count, float wave_phase_degrees,
unsigned extra_cooling)
{
if (flame == NULL || flame->heat == NULL || flame->segments < 2U ||
flame->layers < 2U) return;
if (extra_cooling > 248U) extra_cooling = 248U;
for (unsigned layer = flame->layers - 1U; layer > 0U; --layer) {
for (unsigned segment = 0U; segment < flame->segments; ++segment) {
int source = (int)segment + (int)(radial_flame_random(flame) % 3U) - 1;
if (source < 0) source += (int)flame->segments;
if (source >= (int)flame->segments) source -= (int)flame->segments;
const int heat = flame->heat[(layer - 1U) * flame->segments + (unsigned)source] -
(int)(3U + radial_flame_random(flame) %
(7U + extra_cooling));
flame->heat[layer * flame->segments + segment] = heat > 0 ? (uint8_t)heat : 0U;
}
}
const float phase = wave_phase_degrees * 0.01745329252f;
for (unsigned segment = 0U; segment < flame->segments; ++segment) {
const float position = (float)segment * 6.28318531f * wave_count / flame->segments;
int heat = 170 + (int)(radial_flame_random(flame) % 86U) +
(int)lroundf(sinf(position + phase) * wave_amplitude);
if (heat < 40) heat = 40;
if (heat > 255) heat = 255;
flame->heat[segment] = (uint8_t)heat;
}
}radial_flame_update() · source line 823
void radial_flame_update(radial_flame_t *flame)
Calls into this documentation:
- Execute
radial_flame_update_internal(flame, 0.0f, 1.0f, 0.0f, 0U);
Read original function source
void radial_flame_update(radial_flame_t *flame)
{
radial_flame_update_internal(flame, 0.0f, 1.0f, 0.0f, 0U);
}radial_flame_update_wavy() · source line 828
void radial_flame_update_wavy(radial_flame_t *flame, float wave_amplitude,
float wave_count, float wave_phase_degrees,
unsigned extra_cooling)Calls into this documentation:
- Decision
!isfinite(wave_amplitude) || !isfinite(wave_count) || !isfinite(wave_phase_degrees) || wave_amplitude < 0.0f || wave_count <= 0.0fTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
radial_flame_update_internal(flame, wave_amplitude, wave_count, wave_phase_degrees, extra_cooling);
Read original function source
void radial_flame_update_wavy(radial_flame_t *flame, float wave_amplitude,
float wave_count, float wave_phase_degrees,
unsigned extra_cooling)
{
if (!isfinite(wave_amplitude) || !isfinite(wave_count) ||
!isfinite(wave_phase_degrees) || wave_amplitude < 0.0f ||
wave_count <= 0.0f) return;
radial_flame_update_internal(flame, wave_amplitude, wave_count,
wave_phase_degrees, extra_cooling);
}draw_radial_flame() · source line 839
void draw_radial_flame(uint16_t *frame, const radial_flame_t *flame,
int centre_x, int centre_y, float inner_radius,
float angle_offset_degrees, const uint16_t colours[5])Calls into this documentation:
- Decision
frame == NULL || flame == NULL || flame->heat == NULL || colours == NULL || flame->segments < 2U || flame->layers < 2U || flame->layer_depth <= 0.0f || inner_radius < 0.0f || !isfinite(inner_radius) || !isfinite(angle_offset_degrees)True- Exit this function
return;
FalseContinue after this decision.
- Execute
const float offset = angle_offset_degrees * 0.01745329252f; - Execute
const float angle_step = 6.28318531f / flame->segments; - FOR loop
unsigned layer = 0U; layer < flame->layers; ++layerRepeat body while the loop condition permits; then continue below.
- Execute
const float radius = inner_radius + (float)layer * flame->layer_depth; - FOR loop
unsigned segment = 0U; segment < flame->segments; ++segmentRepeat body while the loop condition permits; then continue below.
- Execute
const uint8_t heat = flame->heat[layer * flame->segments + segment]; - Decision
heat < 40UTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
const unsigned colour = heat < 90U ? 0U : heat < 140U ? 1U : heat < 190U ? 2U : heat < 230U ? 3U : 4U; - Execute
const float first_angle = offset + (float)segment * angle_step; - Execute
const float second_angle = first_angle + angle_step; - Execute
const float inner = radius > 0.75f ? radius - 0.75f : 0.0f; - Execute
const float outer = radius + flame->layer_depth + 0.75f; - Execute
const graphics_point_t wedge[4] = { { centre_x + (int)lroundf(cosf(first_angle) * inner), centre_y + (int)lroundf(sinf(first_angle) * inner) }, { centre_x + (int)lroundf(cosf(second_angle) * inner), centre_y + (int)lroundf(sinf(second_angle) * inner) }, { centre_x + (int)lroundf(cosf(second_angle) * outer), centre_y + (int)lroundf(sinf(second_angle) * outer) }, { centre_x + (int)lroundf(cosf(first_angle) * outer), centre_y + (int)lroundf(sinf(first_angle) * outer) }, }; - Execute
fill_convex_polygon(frame, wedge, 4U, colours[colour]);
Read original function source
void draw_radial_flame(uint16_t *frame, const radial_flame_t *flame,
int centre_x, int centre_y, float inner_radius,
float angle_offset_degrees, const uint16_t colours[5])
{
if (frame == NULL || flame == NULL || flame->heat == NULL || colours == NULL ||
flame->segments < 2U || flame->layers < 2U || flame->layer_depth <= 0.0f ||
inner_radius < 0.0f || !isfinite(inner_radius) ||
!isfinite(angle_offset_degrees)) return;
const float offset = angle_offset_degrees * 0.01745329252f;
const float angle_step = 6.28318531f / flame->segments;
for (unsigned layer = 0U; layer < flame->layers; ++layer) {
const float radius = inner_radius + (float)layer * flame->layer_depth;
for (unsigned segment = 0U; segment < flame->segments; ++segment) {
const uint8_t heat = flame->heat[layer * flame->segments + segment];
if (heat < 40U) continue;
const unsigned colour = heat < 90U ? 0U : heat < 140U ? 1U :
heat < 190U ? 2U : heat < 230U ? 3U : 4U;
const float first_angle = offset + (float)segment * angle_step;
const float second_angle = first_angle + angle_step;
// The overlap removes both radial and angular black seams between cells.
const float inner = radius > 0.75f ? radius - 0.75f : 0.0f;
const float outer = radius + flame->layer_depth + 0.75f;
const graphics_point_t wedge[4] = {
{ centre_x + (int)lroundf(cosf(first_angle) * inner),
centre_y + (int)lroundf(sinf(first_angle) * inner) },
{ centre_x + (int)lroundf(cosf(second_angle) * inner),
centre_y + (int)lroundf(sinf(second_angle) * inner) },
{ centre_x + (int)lroundf(cosf(second_angle) * outer),
centre_y + (int)lroundf(sinf(second_angle) * outer) },
{ centre_x + (int)lroundf(cosf(first_angle) * outer),
centre_y + (int)lroundf(sinf(first_angle) * outer) },
};
fill_convex_polygon(frame, wedge, 4U, colours[colour]);
}
}
}Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "game_graphics.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>
void graphics_text_effect_calculate(graphics_text_effect_t effect, uint32_t elapsed_ms,
unsigned text_length,
const graphics_text_effect_params_t *params,
uint16_t base_color,
const uint16_t *colour_cycle,
unsigned colour_cycle_count,
graphics_text_effect_state_t *state)
{
if (state == NULL) return;
*state = (graphics_text_effect_state_t){
.visible_characters = text_length,
.color = base_color,
.scale = 1.0f,
};
if (params == NULL || effect >= GRAPHICS_TEXT_EFFECT_COUNT) return;
switch (effect) {
case GRAPHICS_TEXT_EFFECT_TRAVEL_LEFT:
if (params->travel_distance > 0) {
const unsigned cycle = (unsigned)params->travel_distance * 2U;
state->x_offset = params->travel_distance -
(int)((elapsed_ms * 55U / 1000U) % cycle);
}
break;
case GRAPHICS_TEXT_EFFECT_DRIFT:
if (params->drift_distance > 0) {
const unsigned cycle = (unsigned)params->drift_distance * 2U;
state->x_offset = (int)((elapsed_ms * 55U / 1000U) % cycle) -
params->drift_distance;
}
break;
case GRAPHICS_TEXT_EFFECT_BOB:
state->y_offset = (int)(sinf((float)elapsed_ms / 445.0f) *
(float)params->vertical_amplitude);
break;
case GRAPHICS_TEXT_EFFECT_WAVE:
state->wave = true;
break;
case GRAPHICS_TEXT_EFFECT_JITTER:
if (params->jitter_amplitude > 0) {
const unsigned range = (unsigned)params->jitter_amplitude * 2U + 1U;
state->x_offset = (int)((elapsed_ms / 18U) % range) -
params->jitter_amplitude;
}
break;
case GRAPHICS_TEXT_EFFECT_REVEAL:
state->visible_characters = (elapsed_ms / 130U) % (text_length + 1U);
break;
case GRAPHICS_TEXT_EFFECT_COLOUR_CYCLE:
if (colour_cycle != NULL && colour_cycle_count > 0U) {
state->color = colour_cycle[(elapsed_ms / 180U) % colour_cycle_count];
}
break;
case GRAPHICS_TEXT_EFFECT_SWING:
state->angle_degrees = sinf((float)elapsed_ms / 382.0f) *
params->maximum_angle_degrees;
break;
case GRAPHICS_TEXT_EFFECT_PULSE:
if (params->minimum_scale > 0.0f &&
params->maximum_scale >= params->minimum_scale) {
const float progress = (sinf((float)elapsed_ms / 318.0f) + 1.0f) * 0.5f;
state->scale = params->minimum_scale +
(params->maximum_scale - params->minimum_scale) * progress;
}
break;
case GRAPHICS_TEXT_EFFECT_DROP:
if (params->drop_height > 0) {
const uint32_t drop_elapsed = elapsed_ms % 2800U;
const int travelled = (int)(drop_elapsed * (uint32_t)params->drop_height / 700U);
state->y_offset = params->drop_height -
(travelled < params->drop_height ? travelled : params->drop_height);
}
break;
case GRAPHICS_TEXT_EFFECT_TYPEWRITER:
state->visible_characters = (elapsed_ms / 90U) % (text_length + 1U);
break;
default:
break;
}
}
int graphics_text_effect_wave_offset(uint32_t elapsed_ms, unsigned character_index,
int wave_amplitude)
{
if (wave_amplitude <= 0) return 0;
const float phase = ((float)(elapsed_ms % 900U) / 900.0f) * 6.2831853f;
return (int)(sinf(phase + (float)character_index * 0.55f) *
(float)wave_amplitude);
}
void clear(uint16_t *frame, uint16_t color)
{
for (size_t pixel = 0; pixel < LCD_WIDTH * LCD_HEIGHT; ++pixel) {
frame[pixel] = color;
}
}
void fill_rect(uint16_t *frame, int x, int y, int width, int height,
uint16_t color)
{
for (int row = y; row < y + height; ++row) {
for (int column = x; column < x + width; ++column) {
if (column >= 0 && column < LCD_WIDTH && row >= 0 && row < LCD_HEIGHT) {
frame[(size_t)row * LCD_WIDTH + column] = color;
}
}
}
}
void draw_rolling_raster_band(uint16_t *frame, int band_top, int block_height,
int band_height, uint32_t elapsed_ms,
uint32_t colour_roll_ms,
const graphics_raster_band_palette_t *palette)
{
if (frame == NULL || palette == NULL || palette->colours == NULL ||
palette->colour_count == 0U || block_height <= 0 || band_height <= 0 ||
colour_roll_ms == 0U) return;
const int stripe_count = (block_height + band_height - 1) / band_height;
const int colour_roll_offset = (int)(elapsed_ms / colour_roll_ms) % stripe_count;
for (int y = band_top, stripe = 0; y < band_top + block_height;
y += band_height, ++stripe) {
const int rolled = (stripe + colour_roll_offset) % stripe_count;
const int palette_index = stripe_count > 1 ?
rolled * ((int)palette->colour_count - 1) / (stripe_count - 1) : 0;
const int visible_height = y + band_height <= band_top + block_height ?
band_height : band_top + block_height - y;
fill_rect(frame, 0, y, LCD_WIDTH, visible_height,
palette->colours[palette_index]);
}
}
static void draw_scaled_image(uint16_t *frame, int x, int y,
const uint16_t *pixels, const uint8_t *mask,
int source_width, int source_height,
int draw_width, int draw_height, uint16_t color)
{
if (frame == NULL || source_width <= 0 || source_height <= 0 ||
draw_width <= 0 || draw_height <= 0) {
return;
}
const size_t mask_stride = ((size_t)source_width + 7U) / 8U;
if ((size_t)source_height > SIZE_MAX / mask_stride ||
(pixels != NULL &&
(size_t)source_height > SIZE_MAX / sizeof(*pixels) / (size_t)source_width)) {
return;
}
// Clip the destination before sampling; retain the original image origin.
const int64_t right = (int64_t)x + draw_width;
const int64_t bottom = (int64_t)y + draw_height;
if (right <= 0 || bottom <= 0 || x >= LCD_WIDTH || y >= LCD_HEIGHT) {
return;
}
const int left = x < 0 ? 0 : x;
const int top = y < 0 ? 0 : y;
const int end_x = right > LCD_WIDTH ? LCD_WIDTH : (int)right;
const int end_y = bottom > LCD_HEIGHT ? LCD_HEIGHT : (int)bottom;
for (int row = top; row < end_y; ++row) {
const size_t source_y = (size_t)(((int64_t)row - y) * source_height / draw_height);
for (int column = left; column < end_x; ++column) {
const size_t source_x = (size_t)(((int64_t)column - x) * source_width / draw_width);
if (mask != NULL &&
(mask[source_y * mask_stride + source_x / 8U] &
(1U << (7U - source_x % 8U))) == 0) {
continue;
}
frame[(size_t)row * LCD_WIDTH + column] = pixels != NULL
? pixels[source_y * (size_t)source_width + source_x] : color;
}
}
}
void draw_bitmap_scaled(uint16_t *frame, int x, int y,
const uint8_t *bitmap, int source_width, int source_height,
int draw_width, int draw_height, uint16_t color)
{
if (bitmap == NULL) {
return;
}
draw_scaled_image(frame, x, y, NULL, bitmap, source_width, source_height,
draw_width, draw_height, color);
}
void draw_rgb565_scaled(uint16_t *frame, int x, int y,
const uint16_t *pixels, const uint8_t *mask,
int source_width, int source_height,
int draw_width, int draw_height)
{
if (pixels == NULL) {
return;
}
draw_scaled_image(frame, x, y, pixels, mask, source_width, source_height,
draw_width, draw_height, 0);
}
static void draw_transformed_image(uint16_t *frame, int destination_centre_x,
int destination_centre_y,
const uint16_t *pixels, const uint8_t *mask,
int source_width, int source_height,
float scale, float angle_degrees, uint16_t color)
{
if (frame == NULL || source_width <= 0 || source_height <= 0 ||
scale <= 0.0f || !isfinite(scale) || !isfinite(angle_degrees)) {
return;
}
const float radians = angle_degrees * 0.01745329252f;
const float cosine = cosf(radians);
const float sine = sinf(radians);
const float half_width = (fabsf(cosine) * source_width +
fabsf(sine) * source_height) * scale * 0.5f + 1.0f;
const float half_height = (fabsf(sine) * source_width +
fabsf(cosine) * source_height) * scale * 0.5f + 1.0f;
const int start_x = (int)floorf(destination_centre_x - half_width);
const int end_x = (int)ceilf(destination_centre_x + half_width);
const int start_y = (int)floorf(destination_centre_y - half_height);
const int end_y = (int)ceilf(destination_centre_y + half_height);
const size_t mask_stride = ((size_t)source_width + 7U) / 8U;
const float source_centre_x = ((float)source_width - 1.0f) * 0.5f;
const float source_centre_y = ((float)source_height - 1.0f) * 0.5f;
for (int destination_y = start_y; destination_y <= end_y; ++destination_y) {
if (destination_y < 0 || destination_y >= LCD_HEIGHT) continue;
const float y = (float)(destination_y - destination_centre_y);
for (int destination_x = start_x; destination_x <= end_x; ++destination_x) {
if (destination_x < 0 || destination_x >= LCD_WIDTH) continue;
const float x = (float)(destination_x - destination_centre_x);
const int source_x = (int)floorf((cosine * x + sine * y) / scale +
source_centre_x + 0.5f);
const int source_y = (int)floorf((-sine * x + cosine * y) / scale +
source_centre_y + 0.5f);
if (source_x < 0 || source_x >= source_width ||
source_y < 0 || source_y >= source_height) continue;
if (mask != NULL &&
(mask[(size_t)source_y * mask_stride + (unsigned)source_x / 8U] &
(1U << (7U - (unsigned)source_x % 8U))) == 0) continue;
frame[(size_t)destination_y * LCD_WIDTH + destination_x] = pixels != NULL
? pixels[(size_t)source_y * source_width + source_x] : color;
}
}
}
void draw_bitmap_transformed(uint16_t *frame, int destination_centre_x,
int destination_centre_y, const uint8_t *bitmap,
int source_width, int source_height,
float scale, float angle_degrees, uint16_t color)
{
if (bitmap == NULL) return;
draw_transformed_image(frame, destination_centre_x, destination_centre_y,
NULL, bitmap, source_width, source_height,
scale, angle_degrees, color);
}
void draw_rgb565_transformed(uint16_t *frame, int destination_centre_x,
int destination_centre_y, const uint16_t *pixels,
const uint8_t *mask, int source_width,
int source_height, float scale,
float angle_degrees)
{
if (pixels == NULL) return;
draw_transformed_image(frame, destination_centre_x, destination_centre_y,
pixels, mask, source_width, source_height,
scale, angle_degrees, 0);
}
void draw_ui_bitmap_8x8(uint16_t *frame, int x, int y,
const uint8_t bitmap[8], int scale, uint16_t color)
{
if (scale <= 0 || scale > INT_MAX / 8) {
return;
}
draw_bitmap_scaled(frame, x, y, bitmap, 8, 8, 8 * scale, 8 * scale, color);
}
void fill_circle(uint16_t *frame, int x, int y, int radius,
uint16_t color)
{
for (int row = -radius; row <= radius; ++row) {
for (int column = -radius; column <= radius; ++column) {
if (column * column + row * row <= radius * radius) {
fill_rect(frame, x + column, y + row, 1, 1, color);
}
}
}
}
// Six-bit rows, ordered as digits followed by uppercase letters. The empty
// eighth row gives the typeface a less crowded baseline than the former 5x7 font.
static const uint8_t ui_glyphs[36][UI_FONT_HEIGHT] = {
{0x1E,0x33,0x37,0x3B,0x33,0x33,0x1E,0x00}, {0x0C,0x1C,0x0C,0x0C,0x0C,0x0C,0x3F,0x00},
{0x1E,0x33,0x03,0x06,0x0C,0x18,0x3F,0x00}, {0x1E,0x33,0x03,0x0E,0x03,0x33,0x1E,0x00},
{0x06,0x0E,0x1E,0x36,0x3F,0x06,0x06,0x00}, {0x3F,0x30,0x3E,0x03,0x03,0x33,0x1E,0x00},
{0x0E,0x18,0x30,0x3E,0x33,0x33,0x1E,0x00}, {0x3F,0x03,0x06,0x0C,0x18,0x18,0x18,0x00},
{0x1E,0x33,0x33,0x1E,0x33,0x33,0x1E,0x00}, {0x1E,0x33,0x33,0x1F,0x03,0x06,0x1C,0x00},
{0x0C,0x1E,0x33,0x33,0x3F,0x33,0x33,0x00}, {0x3E,0x33,0x33,0x3E,0x33,0x33,0x3E,0x00},
{0x1E,0x33,0x30,0x30,0x30,0x33,0x1E,0x00}, {0x3C,0x36,0x33,0x33,0x33,0x36,0x3C,0x00},
{0x3F,0x30,0x30,0x3E,0x30,0x30,0x3F,0x00}, {0x3F,0x30,0x30,0x3E,0x30,0x30,0x30,0x00},
{0x1E,0x33,0x30,0x37,0x33,0x33,0x1F,0x00}, {0x33,0x33,0x33,0x3F,0x33,0x33,0x33,0x00},
{0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00}, {0x07,0x03,0x03,0x03,0x03,0x33,0x1E,0x00},
{0x33,0x36,0x3C,0x38,0x3C,0x36,0x33,0x00}, {0x30,0x30,0x30,0x30,0x30,0x30,0x3F,0x00},
{0x21,0x33,0x3F,0x3F,0x33,0x33,0x33,0x00}, {0x33,0x3B,0x3F,0x3F,0x37,0x33,0x33,0x00},
{0x1E,0x33,0x33,0x33,0x33,0x33,0x1E,0x00}, {0x3E,0x33,0x33,0x3E,0x30,0x30,0x30,0x00},
{0x1E,0x33,0x33,0x33,0x3B,0x36,0x1D,0x00}, {0x3E,0x33,0x33,0x3E,0x3C,0x36,0x33,0x00},
{0x1F,0x30,0x30,0x1E,0x03,0x03,0x3E,0x00}, {0x3F,0x2D,0x0C,0x0C,0x0C,0x0C,0x1E,0x00},
{0x33,0x33,0x33,0x33,0x33,0x33,0x1E,0x00}, {0x33,0x33,0x33,0x33,0x33,0x1E,0x0C,0x00},
{0x33,0x33,0x33,0x3F,0x3F,0x33,0x33,0x00}, {0x33,0x33,0x1E,0x0C,0x1E,0x33,0x33,0x00},
{0x33,0x33,0x1E,0x0C,0x0C,0x0C,0x1E,0x00}, {0x3F,0x03,0x06,0x0C,0x18,0x30,0x3F,0x00},
};
static const uint8_t *ui_glyph(char symbol)
{
static const uint8_t colon[UI_FONT_HEIGHT] = {0,0x0C,0x0C,0,0x0C,0x0C,0,0};
static const uint8_t slash[UI_FONT_HEIGHT] = {0x03,0x06,0x06,0x0C,0x18,0x18,0x30,0};
static const uint8_t minus[UI_FONT_HEIGHT] = {0,0,0,0x3F,0,0,0,0};
static const uint8_t plus[UI_FONT_HEIGHT] = {0,0x0C,0x0C,0x3F,0x0C,0x0C,0,0};
static const uint8_t dot[UI_FONT_HEIGHT] = {0,0,0,0,0,0,0x0C,0};
static const uint8_t apostrophe[UI_FONT_HEIGHT] = {0x0C,0x0C,0x18,0,0,0,0,0};
static const uint8_t question[UI_FONT_HEIGHT] = {0x1E,0x33,0x03,0x06,0x0C,0,0x0C,0};
static const uint8_t exclamation[UI_FONT_HEIGHT] = {0x0C,0x0C,0x0C,0x0C,0x0C,0,0x0C,0};
if (symbol >= 'a' && symbol <= 'z') symbol = (char)(symbol - 'a' + 'A');
if (symbol >= '0' && symbol <= '9') return ui_glyphs[symbol - '0'];
if (symbol >= 'A' && symbol <= 'Z') return ui_glyphs[symbol - 'A' + 10];
switch (symbol) {
case ':': return colon;
case '/': return slash;
case '-': return minus;
case '+': return plus;
case '.': return dot;
case '\'': return apostrophe;
case '?': return question;
case '!': return exclamation;
default: return NULL;
}
}
void draw_text(uint16_t *frame, int x, int y, const char *text,
int scale, uint16_t color)
{
if (frame == NULL || text == NULL || scale <= 0) return;
for (; *text != '\0'; ++text, x += UI_FONT_ADVANCE * scale) {
const uint8_t *glyph = ui_glyph(*text);
if (glyph == NULL) continue;
for (int row = 0; row < UI_FONT_HEIGHT; ++row) {
for (int column = 0; column < UI_FONT_WIDTH; ++column) {
if ((glyph[row] & (1U << (UI_FONT_WIDTH - 1 - column))) != 0)
fill_rect(frame, x + column * scale, y + row * scale,
scale, scale, color);
}
}
}
}
void draw_rect(uint16_t *frame, int x, int y, int width, int height,
uint16_t color)
{
if (width <= 0 || height <= 0) {
return;
}
fill_rect(frame, x, y, width, 1, color);
fill_rect(frame, x, y + height - 1, width, 1, color);
fill_rect(frame, x, y, 1, height, color);
fill_rect(frame, x + width - 1, y, 1, height, color);
}
void draw_line(uint16_t *frame, int x0, int y0, int x1, int y1, uint16_t color)
{
const int dx = abs(x1 - x0);
const int dy = -abs(y1 - y0);
const int sx = x0 < x1 ? 1 : -1;
const int sy = y0 < y1 ? 1 : -1;
int error = dx + dy;
while (true) {
fill_rect(frame, x0, y0, 1, 1, color);
if (x0 == x1 && y0 == y1) {
break;
}
const int twice_error = 2 * error;
if (twice_error >= dy) {
error += dy;
x0 += sx;
}
if (twice_error <= dx) {
error += dx;
y0 += sy;
}
}
}
static uint32_t mix_dotted_seed(uint32_t value)
{
value ^= value >> 16;
value *= 0x7FEB352DU;
value ^= value >> 15;
value *= 0x846CA68BU;
return value ^ (value >> 16);
}
static unsigned greatest_common_divisor(unsigned left, unsigned right)
{
while (right != 0U) {
const unsigned remainder = left % right;
left = right;
right = remainder;
}
return left;
}
static bool dotted_index_is_active(unsigned index, unsigned dot_count,
unsigned dot_density_percent, uint32_t random_seed)
{
if (dot_density_percent == 0U || dot_count == 0U) return false;
if (dot_density_percent >= 100U || dot_count == 1U) return true;
const unsigned active_count = (dot_count * dot_density_percent + 99U) / 100U;
if (active_count >= dot_count) return true;
const unsigned start = mix_dotted_seed(random_seed) % dot_count;
unsigned step = 1U + mix_dotted_seed(random_seed ^ 0x9E3779B9U) % (dot_count - 1U);
while (greatest_common_divisor(step, dot_count) != 1U) {
++step;
if (step >= dot_count) step = 1U;
}
for (unsigned selected = 0U; selected < active_count; ++selected) {
const unsigned candidate = (unsigned)(((uint64_t)start +
(uint64_t)selected * step) % dot_count);
if (candidate == index) return true;
}
return false;
}
static bool dotted_index_is_sparkle(unsigned index, unsigned sparkle_density_percent,
uint32_t random_seed)
{
if (sparkle_density_percent == 0U) return false;
if (sparkle_density_percent >= 100U) return true;
const uint32_t value = mix_dotted_seed(random_seed ^ index * 0x9E3779B9U);
return value % 100U < sparkle_density_percent;
}
void draw_dotted_line_density(uint16_t *frame, int x0, int y0, int x1, int y1,
unsigned dot_spacing, int dot_radius, uint16_t color,
unsigned dot_density_percent, uint32_t random_seed)
{
if (frame == NULL || dot_spacing == 0U || dot_radius < 0 ||
dot_density_percent > 100U) return;
const float delta_x = (float)x1 - (float)x0;
const float delta_y = (float)y1 - (float)y0;
const float line_length = sqrtf(delta_x * delta_x + delta_y * delta_y);
if (line_length == 0.0f) {
if (dotted_index_is_active(0U, 1U, dot_density_percent, random_seed)) {
fill_circle(frame, x0, y0, dot_radius, color);
}
return;
}
const unsigned dot_count = (unsigned)floorf(line_length / (float)dot_spacing) + 1U;
for (unsigned index = 0U; index < dot_count; ++index) {
if (!dotted_index_is_active(index, dot_count, dot_density_percent, random_seed)) {
continue;
}
const float distance = (float)index * (float)dot_spacing;
const float progress = distance / line_length;
const int x = x0 + (int)lroundf(delta_x * progress);
const int y = y0 + (int)lroundf(delta_y * progress);
fill_circle(frame, x, y, dot_radius, color);
}
}
void draw_dotted_line(uint16_t *frame, int x0, int y0, int x1, int y1,
unsigned dot_spacing, int dot_radius, uint16_t color)
{
draw_dotted_line_density(frame, x0, y0, x1, y1, dot_spacing, dot_radius, color,
100U, 0U);
}
static bool packed_bitmap_pixel(const uint8_t *bitmap, unsigned source_width,
unsigned x, unsigned y)
{
const size_t row_bytes = ((size_t)source_width + 7U) / 8U;
const size_t byte_index = (size_t)y * row_bytes + x / 8U;
return (bitmap[byte_index] & (uint8_t)(1U << (7U - x % 8U))) != 0U;
}
typedef struct {
float pitch_cosine;
float pitch_sine;
float yaw_cosine;
float yaw_sine;
float roll_cosine;
float roll_sine;
} dotted_bitmap_rotation_t;
static graphics_point_t project_dotted_bitmap_point(float source_x, float source_y,
float depth,
const dotted_bitmap_3d_params_t *params,
const dotted_bitmap_rotation_t *rotation)
{
const float scaled_x = source_x * params->scale;
const float scaled_y = source_y * params->scale;
const float pitched_y = scaled_y * rotation->pitch_cosine -
depth * rotation->pitch_sine;
const float pitched_z = scaled_y * rotation->pitch_sine +
depth * rotation->pitch_cosine;
const float yawed_x = scaled_x * rotation->yaw_cosine +
pitched_z * rotation->yaw_sine;
const float yawed_z = -scaled_x * rotation->yaw_sine +
pitched_z * rotation->yaw_cosine;
const float rotated_x = yawed_x * rotation->roll_cosine -
pitched_y * rotation->roll_sine;
const float rotated_y = yawed_x * rotation->roll_sine +
pitched_y * rotation->roll_cosine;
const float perspective = params->camera_distance /
(params->camera_distance + yawed_z);
const graphics_point_t point = {
.x = params->centre_x + (int)lroundf(rotated_x * perspective),
.y = params->centre_y + (int)lroundf(rotated_y * perspective),
};
return point;
}
static void draw_dotted_bitmap_depth_line(uint16_t *frame,
float x, float y,
const dotted_bitmap_3d_params_t *params,
const dotted_bitmap_rotation_t *rotation,
uint32_t random_seed)
{
const graphics_point_t front = project_dotted_bitmap_point(x, y, 0.0f,
params, rotation);
const graphics_point_t back = project_dotted_bitmap_point(x, y, params->depth,
params, rotation);
draw_dotted_line_density(frame, front.x, front.y, back.x, back.y,
params->depth_dot_spacing, params->dot_radius,
params->side_color, params->dot_density_percent,
random_seed);
}
static void draw_dotted_bitmap_face(uint16_t *frame, const uint8_t *bitmap,
unsigned source_width, unsigned source_height,
float depth, uint16_t color,
const dotted_bitmap_3d_params_t *params,
const dotted_bitmap_rotation_t *rotation,
uint32_t random_seed,
bool draw_sparkles)
{
const float half_width = (float)source_width * 0.5f;
const float half_height = (float)source_height * 0.5f;
for (unsigned y = 0U; y < source_height; ++y) {
unsigned run_start = 0U;
while (run_start < source_width) {
while (run_start < source_width &&
!packed_bitmap_pixel(bitmap, source_width, run_start, y)) {
++run_start;
}
if (run_start == source_width) break;
unsigned run_end = run_start + 1U;
while (run_end < source_width &&
packed_bitmap_pixel(bitmap, source_width, run_end, y)) {
++run_end;
}
const unsigned dot_count = run_end - run_start;
const uint32_t line_seed = random_seed ^ y * 0x45D9F3BU ^
run_start * 0x119DE1F3U;
for (unsigned index = 0U; index < dot_count; ++index) {
if (!dotted_index_is_active(index, dot_count,
params->dot_density_percent, line_seed)) {
continue;
}
const float source_x = (float)(run_start + index) + 0.5f - half_width;
const float source_y = (float)y + 0.5f - half_height;
const graphics_point_t dot = project_dotted_bitmap_point(source_x, source_y,
depth, params,
rotation);
fill_circle(frame, dot.x, dot.y, params->dot_radius, color);
if (draw_sparkles && params->sparkle_density_percent != 0U &&
dotted_index_is_sparkle(index, params->sparkle_density_percent,
line_seed ^ params->sparkle_phase * 0x27D4EB2DU)) {
fill_circle(frame, dot.x, dot.y, params->sparkle_radius,
params->sparkle_color);
}
}
run_start = run_end;
}
}
}
void draw_bitmap_dotted_3d(uint16_t *frame, const uint8_t *bitmap,
unsigned source_width, unsigned source_height,
const dotted_bitmap_3d_params_t *params)
{
if (frame == NULL || bitmap == NULL || params == NULL || source_width == 0U ||
source_height == 0U || params->scale <= 0.0f || params->depth <= 0.0f ||
params->camera_distance <= 0.0f || params->depth_dot_spacing == 0U ||
params->dot_radius < 0 || params->dot_density_percent > 100U ||
params->sparkle_density_percent > 100U || params->sparkle_radius < 0 ||
!isfinite(params->scale) || !isfinite(params->pitch_degrees) ||
!isfinite(params->yaw_degrees) || !isfinite(params->roll_degrees) ||
!isfinite(params->depth) ||
!isfinite(params->camera_distance)) {
return;
}
const float half_width = (float)source_width * params->scale * 0.5f;
const float half_height = (float)source_height * params->scale * 0.5f;
const float safe_camera_distance = sqrtf(half_width * half_width +
half_height * half_height +
params->depth * params->depth);
if (params->camera_distance <= safe_camera_distance) {
return;
}
const float pitch_radians = params->pitch_degrees * 0.01745329252f;
const float yaw_radians = params->yaw_degrees * 0.01745329252f;
const float roll_radians = params->roll_degrees * 0.01745329252f;
const dotted_bitmap_rotation_t rotation = {
.pitch_cosine = cosf(pitch_radians),
.pitch_sine = sinf(pitch_radians),
.yaw_cosine = cosf(yaw_radians),
.yaw_sine = sinf(yaw_radians),
.roll_cosine = cosf(roll_radians),
.roll_sine = sinf(roll_radians),
};
const float source_half_width = (float)source_width * 0.5f;
const float source_half_height = (float)source_height * 0.5f;
// Draw the back first, then edge joins, then front, so the transparent
// dotted volume remains readable over the caller's existing background.
draw_dotted_bitmap_face(frame, bitmap, source_width, source_height,
params->depth, params->back_color, params, &rotation,
params->random_seed ^ 0xA511E9B3U, false);
for (unsigned y = 0U; y < source_height; ++y) {
unsigned run_start = 0U;
while (run_start < source_width) {
while (run_start < source_width &&
!packed_bitmap_pixel(bitmap, source_width, run_start, y)) {
++run_start;
}
if (run_start == source_width) break;
unsigned run_end = run_start + 1U;
while (run_end < source_width &&
packed_bitmap_pixel(bitmap, source_width, run_end, y)) {
++run_end;
}
const float edge_y = (float)y - source_half_height;
draw_dotted_bitmap_depth_line(frame, (float)run_start - source_half_width, edge_y,
params, &rotation,
params->random_seed ^ y * 0x45D9F3BU ^
run_start * 0x119DE1F3U ^ 0x3C6EF372U);
draw_dotted_bitmap_depth_line(frame, (float)run_end - source_half_width, edge_y,
params, &rotation,
params->random_seed ^ y * 0x45D9F3BU ^
run_end * 0x119DE1F3U ^ 0xDAA66D2BU);
run_start = run_end;
}
}
draw_dotted_bitmap_face(frame, bitmap, source_width, source_height,
0.0f, params->front_color, params, &rotation,
params->random_seed ^ 0x63D83595U, true);
}
void fill_convex_polygon(uint16_t *frame, const graphics_point_t *points,
size_t point_count, uint16_t color)
{
if (frame == NULL || points == NULL || point_count < 3U) return;
int minimum_y = points[0].y;
int maximum_y = points[0].y;
for (size_t point = 1U; point < point_count; ++point) {
if (points[point].y < minimum_y) minimum_y = points[point].y;
if (points[point].y > maximum_y) maximum_y = points[point].y;
}
if (minimum_y < 0) minimum_y = 0;
if (maximum_y >= LCD_HEIGHT) maximum_y = LCD_HEIGHT - 1;
for (int y = minimum_y; y <= maximum_y; ++y) {
int left = LCD_WIDTH;
int right = -1;
for (size_t point = 0U; point < point_count; ++point) {
const graphics_point_t first = points[point];
const graphics_point_t second = points[(point + 1U) % point_count];
const int edge_min_y = first.y < second.y ? first.y : second.y;
const int edge_max_y = first.y > second.y ? first.y : second.y;
if (edge_min_y == edge_max_y || y < edge_min_y || y >= edge_max_y) continue;
const int x = first.x + (y - first.y) * (second.x - first.x) /
(second.y - first.y);
if (x < left) left = x;
if (x > right) right = x;
}
if (right < 0 || left >= LCD_WIDTH) continue;
if (left < 0) left = 0;
if (right >= LCD_WIDTH) right = LCD_WIDTH - 1;
if (left <= right) fill_rect(frame, left, y, right - left + 1, 1, color);
}
}
void draw_ring(uint16_t *frame, int x, int y, int radius, uint16_t color)
{
for (int row = -radius; row <= radius; ++row) {
for (int column = -radius; column <= radius; ++column) {
const int distance = column * column + row * row;
if (distance <= radius * radius &&
(radius == 0 || distance > (radius - 1) * (radius - 1))) {
fill_rect(frame, x + column, y + row, 1, 1, color);
}
}
}
}
void draw_text_centred(uint16_t *frame, int x, int y, const char *text,
int scale, uint16_t color)
{
const int length = (int)strlen(text);
const int width = length > 0 ? length * UI_FONT_ADVANCE * scale - scale : 0;
draw_text(frame, x - width / 2, y, text, scale, color);
}
void draw_text_transformed(uint16_t *frame, int destination_centre_x,
int destination_centre_y, const char *text,
int source_scale, float transform_scale,
float angle_degrees, uint16_t color)
{
if (frame == NULL || text == NULL || source_scale <= 0 ||
transform_scale <= 0.0f || !isfinite(transform_scale) ||
!isfinite(angle_degrees)) return;
int length = 0;
while (text[length] != '\0') ++length;
if (length == 0) return;
const int source_width = length * UI_FONT_ADVANCE * source_scale - source_scale;
const int source_height = UI_FONT_HEIGHT * source_scale;
const float radians = angle_degrees * 0.01745329252f;
const float cosine = cosf(radians), sine = sinf(radians);
const float centre_x = ((float)source_width - 1.0f) * 0.5f;
const float centre_y = ((float)source_height - 1.0f) * 0.5f;
int glyph_x = 0;
for (int character = 0; character < length; ++character, glyph_x += UI_FONT_ADVANCE * source_scale) {
const uint8_t *glyph = ui_glyph(text[character]);
if (glyph == NULL) continue;
for (int row = 0; row < UI_FONT_HEIGHT; ++row) for (int column = 0; column < UI_FONT_WIDTH; ++column) {
if ((glyph[row] & (1U << (UI_FONT_WIDTH - 1 - column))) == 0) continue;
for (int pixel_y = 0; pixel_y < source_scale; ++pixel_y)
for (int pixel_x = 0; pixel_x < source_scale; ++pixel_x) {
const float source_x = glyph_x + column * source_scale + pixel_x - centre_x;
const float source_y = row * source_scale + pixel_y - centre_y;
const int destination_x = (int)lroundf(destination_centre_x +
(cosine * source_x - sine * source_y) * transform_scale);
const int destination_y = (int)lroundf(destination_centre_y +
(sine * source_x + cosine * source_y) * transform_scale);
if (destination_x >= 0 && destination_x < LCD_WIDTH &&
destination_y >= 0 && destination_y < LCD_HEIGHT)
frame[(size_t)destination_y * LCD_WIDTH + destination_x] = color;
}
}
}
}
static uint32_t radial_flame_random(radial_flame_t *flame)
{
flame->random_state ^= flame->random_state << 13;
flame->random_state ^= flame->random_state >> 17;
flame->random_state ^= flame->random_state << 5;
return flame->random_state;
}
void radial_flame_initialize(radial_flame_t *flame, uint8_t *heat,
unsigned segments, unsigned layers,
float layer_depth, uint32_t seed)
{
if (flame == NULL || heat == NULL || segments < 2U || layers < 2U ||
layer_depth <= 0.0f || !isfinite(layer_depth)) return;
flame->heat = heat;
flame->segments = segments;
flame->layers = layers;
flame->layer_depth = layer_depth;
flame->random_state = seed == 0U ? 0x6D2B79F5U : seed;
memset(heat, 0, (size_t)segments * layers);
}
static void radial_flame_update_internal(radial_flame_t *flame, float wave_amplitude,
float wave_count, float wave_phase_degrees,
unsigned extra_cooling)
{
if (flame == NULL || flame->heat == NULL || flame->segments < 2U ||
flame->layers < 2U) return;
if (extra_cooling > 248U) extra_cooling = 248U;
for (unsigned layer = flame->layers - 1U; layer > 0U; --layer) {
for (unsigned segment = 0U; segment < flame->segments; ++segment) {
int source = (int)segment + (int)(radial_flame_random(flame) % 3U) - 1;
if (source < 0) source += (int)flame->segments;
if (source >= (int)flame->segments) source -= (int)flame->segments;
const int heat = flame->heat[(layer - 1U) * flame->segments + (unsigned)source] -
(int)(3U + radial_flame_random(flame) %
(7U + extra_cooling));
flame->heat[layer * flame->segments + segment] = heat > 0 ? (uint8_t)heat : 0U;
}
}
const float phase = wave_phase_degrees * 0.01745329252f;
for (unsigned segment = 0U; segment < flame->segments; ++segment) {
const float position = (float)segment * 6.28318531f * wave_count / flame->segments;
int heat = 170 + (int)(radial_flame_random(flame) % 86U) +
(int)lroundf(sinf(position + phase) * wave_amplitude);
if (heat < 40) heat = 40;
if (heat > 255) heat = 255;
flame->heat[segment] = (uint8_t)heat;
}
}
void radial_flame_update(radial_flame_t *flame)
{
radial_flame_update_internal(flame, 0.0f, 1.0f, 0.0f, 0U);
}
void radial_flame_update_wavy(radial_flame_t *flame, float wave_amplitude,
float wave_count, float wave_phase_degrees,
unsigned extra_cooling)
{
if (!isfinite(wave_amplitude) || !isfinite(wave_count) ||
!isfinite(wave_phase_degrees) || wave_amplitude < 0.0f ||
wave_count <= 0.0f) return;
radial_flame_update_internal(flame, wave_amplitude, wave_count,
wave_phase_degrees, extra_cooling);
}
void draw_radial_flame(uint16_t *frame, const radial_flame_t *flame,
int centre_x, int centre_y, float inner_radius,
float angle_offset_degrees, const uint16_t colours[5])
{
if (frame == NULL || flame == NULL || flame->heat == NULL || colours == NULL ||
flame->segments < 2U || flame->layers < 2U || flame->layer_depth <= 0.0f ||
inner_radius < 0.0f || !isfinite(inner_radius) ||
!isfinite(angle_offset_degrees)) return;
const float offset = angle_offset_degrees * 0.01745329252f;
const float angle_step = 6.28318531f / flame->segments;
for (unsigned layer = 0U; layer < flame->layers; ++layer) {
const float radius = inner_radius + (float)layer * flame->layer_depth;
for (unsigned segment = 0U; segment < flame->segments; ++segment) {
const uint8_t heat = flame->heat[layer * flame->segments + segment];
if (heat < 40U) continue;
const unsigned colour = heat < 90U ? 0U : heat < 140U ? 1U :
heat < 190U ? 2U : heat < 230U ? 3U : 4U;
const float first_angle = offset + (float)segment * angle_step;
const float second_angle = first_angle + angle_step;
// The overlap removes both radial and angular black seams between cells.
const float inner = radius > 0.75f ? radius - 0.75f : 0.0f;
const float outer = radius + flame->layer_depth + 0.75f;
const graphics_point_t wedge[4] = {
{ centre_x + (int)lroundf(cosf(first_angle) * inner),
centre_y + (int)lroundf(sinf(first_angle) * inner) },
{ centre_x + (int)lroundf(cosf(second_angle) * inner),
centre_y + (int)lroundf(sinf(second_angle) * inner) },
{ centre_x + (int)lroundf(cosf(second_angle) * outer),
centre_y + (int)lroundf(sinf(second_angle) * outer) },
{ centre_x + (int)lroundf(cosf(first_angle) * outer),
centre_y + (int)lroundf(sinf(first_angle) * outer) },
};
fill_convex_polygon(frame, wedge, 4U, colours[colour]);
}
}
}