#include "graphics2d/c_api.h"
#include <stdio.h>
#define CHECK_STEP(expr, code, label) \
do { \
status = (expr); \
if (status != G2D_STATUS_OK) { \
fprintf(stderr, "%s failed: %s\n", (label), g2d_status_to_string(status)); \
g2d_surface_destroy(surface); \
g2d_context_destroy(context); \
return (code); \
} \
} while (0)
int main(void) {
g2d_context_t* context = NULL;
g2d_surface_t* surface = NULL;
g2d_display_list_builder_t* dl_builder = NULL;
g2d_display_list_t* dl = NULL;
const g2d_context_desc_t context_desc = {
.backend = G2D_BACKEND_SOFTWARE,
.enable_validation = false,
};
const g2d_surface_desc_t surface_desc = {
.width = 3840,
.height = 2160,
};
g2d_status_t status = g2d_context_create(&context_desc, &context);
if (status != G2D_STATUS_OK) {
fprintf(stderr, "g2d_context_create failed: %s\n", g2d_status_to_string(status));
return 1;
}
status = g2d_surface_create_raster(context, &surface_desc, &surface);
if (status != G2D_STATUS_OK) {
fprintf(stderr, "g2d_surface_create_raster failed: %s\n", g2d_status_to_string(status));
g2d_context_destroy(context);
return 2;
}
CHECK_STEP(g2d_surface_clear_rgba8(surface, 10, 12, 18, 255), 3,
"g2d_surface_clear_rgba8");
// Vertical gradient bands (simulated gradient with many filled strips).
for (int y = 0; y < 2160; y += 8) {
const int t = (y * 255) / 2159;
const uint8_t r = (uint8_t)(12 + (t * 18) / 255);
const uint8_t g = (uint8_t)(18 + (t * 44) / 255);
const uint8_t b = (uint8_t)(30 + (t * 88) / 255);
CHECK_STEP(g2d_surface_fill_rect_rgba8(surface, 0, y, 3840, 8, r, g, b, 255), 4,
"g2d_surface_fill_rect_rgba8(gradient)");
}
// Soft center glow.
for (int radius = 1100; radius >= 120; radius -= 28) {
const int d = 1100 - radius;
const uint8_t a = (uint8_t)(10 + (d * 55) / 980);
CHECK_STEP(g2d_surface_fill_circle_rgba8(surface, 1920, 920, (uint32_t)radius, 70, 130,
220, a),
5, "g2d_surface_fill_circle_rgba8(glow)");
}
// Grid lines for depth.
for (int x = 0; x < 3840; x += 160) {
CHECK_STEP(g2d_surface_draw_line_rgba8(surface, x, 0, x, 2159, 1, 255, 255, 255, 30), 6,
"g2d_surface_draw_line_rgba8(grid-v)");
}
for (int y = 0; y < 2160; y += 120) {
CHECK_STEP(g2d_surface_draw_line_rgba8(surface, 0, y, 3839, y, 1, 255, 255, 255, 24), 7,
"g2d_surface_draw_line_rgba8(grid-h)");
}
// Main cards/panels.
CHECK_STEP(g2d_surface_fill_rect_rgba8(surface, 180, 120, 1180, 660, 240, 190, 80, 220), 8,
"g2d_surface_fill_rect_rgba8(panel-a)");
CHECK_STEP(g2d_surface_draw_rect_rgba8(surface, 160, 100, 1220, 700, 14, 255, 255, 255, 255),
9, "g2d_surface_draw_rect_rgba8(panel-a)");
CHECK_STEP(g2d_surface_fill_rect_rgba8(surface, 2460, 260, 1160, 760, 120, 210, 255, 195), 10,
"g2d_surface_fill_rect_rgba8(panel-b)");
CHECK_STEP(g2d_surface_draw_rect_rgba8(surface, 2430, 230, 1220, 820, 14, 255, 255, 255, 240),
11, "g2d_surface_draw_rect_rgba8(panel-b)");
CHECK_STEP(g2d_surface_fill_round_rect_rgba8(surface, 420, 340, 760, 300, 56, 56, 40, 70, 120,
205),
12, "g2d_surface_fill_round_rect_rgba8(card)");
CHECK_STEP(g2d_surface_draw_round_rect_rgba8(surface, 390, 312, 820, 356, 72, 72, 8, 255, 255,
255, 220),
13, "g2d_surface_draw_round_rect_rgba8(card)");
// Circles and rings.
CHECK_STEP(g2d_surface_fill_circle_rgba8(surface, 2860, 620, 250, 120, 210, 255, 230), 14,
"g2d_surface_fill_circle_rgba8(main)");
CHECK_STEP(g2d_surface_draw_circle_rgba8(surface, 2860, 620, 340, 16, 255, 255, 255, 245), 15,
"g2d_surface_draw_circle_rgba8(main)");
for (int r = 420; r <= 760; r += 85) {
CHECK_STEP(g2d_surface_draw_circle_rgba8(surface, 2860, 620, (uint32_t)r, 4, 180, 220, 255,
115),
16, "g2d_surface_draw_circle_rgba8(rings)");
}
// Burst lines.
for (int i = 0; i <= 12; i++) {
const int x0 = 1920 + (i - 6) * 280;
const int y0 = 260;
const int x1 = 1900 + (i - 6) * 340;
const int y1 = 1960;
CHECK_STEP(g2d_surface_draw_line_rgba8(surface, x0, y0, x1, y1, 3, 255, 255, 255, 90), 17,
"g2d_surface_draw_line_rgba8(burst)");
}
// Curves and path strokes.
CHECK_STEP(g2d_surface_draw_quadratic_bezier_rgba8(surface, 220, 980, 1180, 620, 2100, 1120, 12,
255, 170, 120, 230),
18, "g2d_surface_draw_quadratic_bezier_rgba8(curve-a)");
CHECK_STEP(g2d_surface_draw_quadratic_bezier_rgba8(surface, 200, 1160, 1180, 760, 2120, 1280,
5, 255, 240, 220, 180),
19, "g2d_surface_draw_quadratic_bezier_rgba8(curve-b)");
CHECK_STEP(g2d_surface_draw_cubic_bezier_rgba8(surface, 1420, 320, 1780, 20, 3340, 1220, 3620,
880, 9, 120, 240, 255, 235),
20, "g2d_surface_draw_cubic_bezier_rgba8(cubic-a)");
CHECK_STEP(g2d_surface_draw_cubic_bezier_rgba8(surface, 1380, 500, 1860, 220, 3360, 1400, 3600,
1120, 4, 235, 255, 255, 170),
21, "g2d_surface_draw_cubic_bezier_rgba8(cubic-b)");
const g2d_point_i32_t zig_path[] = {
{360, 1240}, {540, 1110}, {720, 1275}, {900, 1140}, {1080, 1310},
{1260, 1180}, {1440, 1350}, {1620, 1210}, {1800, 1395},
};
CHECK_STEP(g2d_surface_draw_path_rgba8(surface, zig_path,
(uint32_t)(sizeof(zig_path) / sizeof(zig_path[0])),
false, 8, 150, 255, 190, 230),
22, "g2d_surface_draw_path_rgba8(zig)");
const g2d_point_i32_t poly_loop[] = {
{2350, 1260}, {2530, 1120}, {2800, 1135}, {2970, 1305},
{2900, 1540}, {2620, 1600}, {2400, 1470},
};
CHECK_STEP(g2d_surface_draw_path_rgba8(surface, poly_loop,
(uint32_t)(sizeof(poly_loop) / sizeof(poly_loop[0])),
true, 6, 255, 220, 120, 210),
23, "g2d_surface_draw_path_rgba8(loop)");
// Small decorative nodes.
for (int i = 0; i < 18; i++) {
const int x = 220 + i * 200;
const int y = 1840 + (i % 3) * 70;
const int rr = 10 + (i % 4) * 5;
CHECK_STEP(g2d_surface_fill_circle_rgba8(surface, x, y, (uint32_t)rr, 255, 210, 120, 230),
24, "g2d_surface_fill_circle_rgba8(nodes)");
}
CHECK_STEP(g2d_surface_draw_text_utf8_rgba8(surface, 180, 1420, 7, "GRAPHICS2D 4K SCENE",
255, 255, 255, 255),
25, "g2d_surface_draw_text_utf8_rgba8(title)");
CHECK_STEP(g2d_surface_draw_text_utf8_rgba8(surface, 184, 1540, 4,
"more shapes roundrects curves paths",
220, 235, 255, 255),
26, "g2d_surface_draw_text_utf8_rgba8(subtitle)");
CHECK_STEP(g2d_surface_draw_text_utf8_rgba8_font_file(
surface, 180, 1668, 4, "Inter Variable - headline",
"assets/fonts/Inter-4.1/InterVariable.ttf", 255, 245, 210, 255),
27, "g2d_surface_draw_text_utf8_rgba8_font_file(inter)");
CHECK_STEP(g2d_surface_draw_text_utf8_rgba8_font_file(
surface, 180, 1752, 3, "IBM Plex Sans - system label",
"assets/fonts/IBM_Plex_Sans/IBMPlexSans-VariableFont_wdth,wght.ttf", 215,
238, 255, 245),
28, "g2d_surface_draw_text_utf8_rgba8_font_file(plex)");
CHECK_STEP(g2d_surface_draw_text_utf8_rgba8_font_file(
surface, 180, 1826, 3, "JetBrains Mono - diagnostics",
"assets/fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-Bold.ttf", 190,
255, 220, 240),
29, "g2d_surface_draw_text_utf8_rgba8_font_file(jetbrains)");
CHECK_STEP(g2d_surface_draw_text_utf8_rgba8_aligned(surface, 2860, 620, 3, "rings + glow",
G2D_TEXT_ALIGN_CENTER,
G2D_TEXT_BASELINE_MIDDLE, 10, 22, 40, 245),
30, "g2d_surface_draw_text_utf8_rgba8_aligned(node-label)");
const char* app_icons[] = {
"assets/icons/src/scalable/apps/blender.svg",
"assets/icons/src/scalable/apps/chromium-browser.svg",
"assets/icons/src/scalable/apps/cmake.svg",
"assets/icons/src/scalable/apps/audacity.svg",
"assets/icons/src/scalable/apps/arduino.svg",
"assets/icons/src/scalable/apps/calibre.svg",
"assets/icons/src/scalable/apps/atom.svg",
"assets/icons/src/scalable/apps/clementine.svg",
"assets/icons/src/scalable/apps/cheese.svg",
"assets/icons/src/scalable/apps/anki.svg",
"assets/icons/src/scalable/apps/bitcoin.svg",
"assets/icons/src/scalable/apps/brave.svg",
};
for (int i = 0; i < 12; i++) {
const int col = i % 6;
const int row = i / 6;
const int px = 1460 + col * 380;
const int py = 1200 + row * 430;
CHECK_STEP(g2d_surface_draw_svg_file(surface, app_icons[i], px, py, 260, 260, 235), 31,
"g2d_surface_draw_svg_file(icon)");
CHECK_STEP(g2d_surface_draw_rect_rgba8(surface, px - 16, py - 16, 292, 292, 4, 255, 255,
255, 90),
32, "g2d_surface_draw_rect_rgba8(icon-frame)");
}
status = g2d_display_list_builder_create(&dl_builder);
if (status != G2D_STATUS_OK) {
fprintf(stderr, "g2d_display_list_builder_create failed: %s\n", g2d_status_to_string(status));
g2d_surface_destroy(surface);
g2d_context_destroy(context);
return 33;
}
CHECK_STEP(g2d_display_list_builder_fill_rect_rgba8(dl_builder, 0, 0, 760, 220, 16, 24, 40, 170),
34, "g2d_display_list_builder_fill_rect_rgba8");
CHECK_STEP(g2d_display_list_builder_draw_round_rect_rgba8(dl_builder, 0, 0, 760, 220, 34, 34, 6,
255, 255, 255, 220),
35, "g2d_display_list_builder_draw_round_rect_rgba8");
const g2d_point_i32_t dl_wave[] = {
{30, 140}, {120, 70}, {220, 154}, {330, 62}, {440, 165}, {560, 76}, {690, 145},
};
CHECK_STEP(g2d_display_list_builder_draw_path_rgba8(
dl_builder, dl_wave, (uint32_t)(sizeof(dl_wave) / sizeof(dl_wave[0])), false,
7, 140, 255, 200, 235),
36, "g2d_display_list_builder_draw_path_rgba8");
status = g2d_display_list_builder_build(dl_builder, &dl);
if (status != G2D_STATUS_OK) {
fprintf(stderr, "g2d_display_list_builder_build failed: %s\n", g2d_status_to_string(status));
g2d_display_list_builder_destroy(dl_builder);
g2d_surface_destroy(surface);
g2d_context_destroy(context);
return 37;
}
CHECK_STEP(g2d_surface_draw_display_list(surface, dl, 220, 1890), 38,
"g2d_surface_draw_display_list(a)");
CHECK_STEP(g2d_surface_draw_display_list(surface, dl, 2880, 1860), 39,
"g2d_surface_draw_display_list(b)");
CHECK_STEP(g2d_surface_draw_text_utf8_rgba8_aligned(
surface, 1920, 2040, 4, "assets/smoke_scene_4k.ppm", G2D_TEXT_ALIGN_CENTER,
G2D_TEXT_BASELINE_BOTTOM, 255, 220, 120, 255),
40, "g2d_surface_draw_text_utf8_rgba8_aligned(footer)");
status = g2d_surface_save_ppm(surface, "assets/smoke_scene_4k.ppm");
if (status != G2D_STATUS_OK) {
fprintf(stderr, "g2d_surface_save_ppm failed: %s\n", g2d_status_to_string(status));
g2d_surface_destroy(surface);
g2d_context_destroy(context);
g2d_display_list_destroy(dl);
g2d_display_list_builder_destroy(dl_builder);
return 41;
}
status = g2d_surface_save_png(surface, "assets/smoke_scene_4k.png");
if (status != G2D_STATUS_OK && status != G2D_STATUS_NOT_IMPLEMENTED) {
fprintf(stderr, "g2d_surface_save_png failed: %s\n", g2d_status_to_string(status));
g2d_surface_destroy(surface);
g2d_context_destroy(context);
g2d_display_list_destroy(dl);
g2d_display_list_builder_destroy(dl_builder);
return 42;
}
printf("Wrote assets/smoke_scene_4k.ppm (%ux%u).\n",
g2d_surface_get_width(surface),
g2d_surface_get_height(surface));
if (status == G2D_STATUS_OK) {
printf("Wrote assets/smoke_scene_4k.png.\n");
} else {
printf("PNG writer unavailable in this build.\n");
}
g2d_display_list_destroy(dl);
g2d_display_list_builder_destroy(dl_builder);
g2d_surface_destroy(surface);
g2d_context_destroy(context);
return 0;
}