diff options
| author | Syndamia <kamen@syndamia.com> | 2026-03-14 22:39:45 +0200 |
|---|---|---|
| committer | Syndamia <kamen@syndamia.com> | 2026-03-14 23:07:10 +0200 |
| commit | 5b42ff05f4cea7ba5014191ff4a6c8f60d822958 (patch) | |
| tree | 237b8cadb872f0a455179e6752616823ae13308c /graphics.c | |
| parent | 508bc660f5bdd05ef35662b3acc19d47f1ebb6d9 (diff) | |
| download | ppm_graphics-5b42ff05f4cea7ba5014191ff4a6c8f60d822958.tar ppm_graphics-5b42ff05f4cea7ba5014191ff4a6c8f60d822958.tar.gz ppm_graphics-5b42ff05f4cea7ba5014191ff4a6c8f60d822958.zip | |
feat: Implement image and line processing, value interpolation
Diffstat (limited to 'graphics.c')
| -rw-r--r-- | graphics.c | 201 |
1 files changed, 190 insertions, 11 deletions
@@ -1,3 +1,7 @@ +/* gcc -std=c99 graphics.c -lm && ./a.out | mpv --no-correct-pts --container-fps-override=30 - + * gcc -std=c99 graphics.c -lm && ./a.out | ffmpeg -r 30 -i - output.mp4 + */ + #include <stdio.h> #include <stdlib.h> #include <stdint.h> @@ -91,6 +95,22 @@ ppm6_write(FILE* f, RGBImage img) { return fflush(f); } +RGBImage +ppm_read(FILE* f) { + u32 type, width, height, colors; + fscanf(f, "P%d\n%d %d\n%d\n", &type, &width, &height, &colors); + + RGBImage img = RGBImage_new(width, height); + ARGB* pixel = img.img; + for (size_t i = img.width * img.height; i > 0; --i, ++pixel) { + if (type == 3) { + pixel->a = 0xFF; + fscanf(f, "%d %d %d ", &pixel->r, &pixel->g, &pixel->b); + } + } + return img; +} + ////Animation//// struct AnimationEventNode; @@ -100,6 +120,7 @@ typedef struct Animation { u32 width; u32 height; u32 frameCount; + u32 duplicateFrames; RGBImage frameBuffer; AnimationEventNode* events; @@ -134,6 +155,7 @@ Animation_new(u32 width, u32 height, u32 frameCount) { .width = width, .height = height, .frameCount = frameCount, + .duplicateFrames = 0, .frameBuffer = RGBImage_new(width, height), .events = NULL, }; @@ -208,6 +230,9 @@ Animation_render(Animation* anim) { } ppm6_write(stdout, anim->frameBuffer); + + for (u32 duplicates = 0; duplicates < anim->duplicateFrames; ++duplicates) + ppm6_write(stdout, anim->frameBuffer); } } @@ -253,30 +278,104 @@ Square(const Animation* anim, u32 frameIndex, ARGB pixel, u32 r, u32 c, void* pr return pixel; } +struct Image { + RGBImage img; + double zoom; + bool noRepeat; +}; + +ARGB +Image(const Animation* anim, u32 frameIndex, ARGB pixel, u32 r, u32 c, void* priv) { + struct Image img = *(struct Image*)priv; + + if (img.noRepeat == true && + (img.img.width * img.zoom <= c || + img.img.height * img.zoom <= r)) + { + return pixel; + } + + return *RGBImage_at(*(RGBImage*)priv, r / img.zoom, c / img.zoom); +} + +struct Line { + double a; + double b; + double c; + double width; + byte4 color; +}; + +ARGB +Line(const Animation* anim, u32 frameIndex, ARGB pixel, u32 r, u32 c, void* priv) { + struct Line l = *(struct Line*)priv; + + if (fabs(l.a * c + l.b * r + l.c) / sqrt(l.a * l.a + l.b * l.b) < l.width) { + ARGB_set(&pixel, l.color); + } + + return pixel; +} + +#define NUM_INTERPOLATE(a, b, perc) (a * (1.0 - (perc)) + b * (perc)) + +byte4 +colorInterpolate(byte4 ina, byte4 inb, double percentage) { + ARGB a, b; + ARGB_set(&a, ina); + ARGB_set(&b, inb); + + a.a = NUM_INTERPOLATE(a.a, b.a, percentage); + a.r = NUM_INTERPOLATE(a.r, b.r, percentage); + a.g = NUM_INTERPOLATE(a.g, b.g, percentage); + a.b = NUM_INTERPOLATE(a.b, b.b, percentage); + + return a.a << 24 | a.r << 16 | a.g << 8 | a.b; +} + +void +LineInterpolate(void* valueLine, const void* initialLine, const void* finalLine, double percentage) { + struct Line* value = valueLine; + const struct Line* initial = initialLine, *final = finalLine; + value->a = NUM_INTERPOLATE(initial->a, final->a, percentage); + value->b = NUM_INTERPOLATE(initial->b, final->b, percentage); + value->c = NUM_INTERPOLATE(initial->c, final->c, percentage); + value->width = NUM_INTERPOLATE(initial->width, final->width, percentage); + value->color = colorInterpolate(initial->color, final->color, percentage); +} + struct MoveLinear { + PixelRenderer callback; + void* priv; + u32 startRow; u32 startCol; float dRow; float dCol; - PixelRenderer callback; - void* priv; + bool wrapCoordinates; }; ARGB MoveLinear(const Animation* anim, u32 frameIndex, ARGB pixel, u32 r, u32 c, void* priv) { struct MoveLinear ml = *(struct MoveLinear*)priv; - r = (r + ml.startRow - (u32)(frameIndex * ml.dRow)) % anim->width; - c = (c + ml.startCol - (u32)(frameIndex * ml.dCol)) % anim->height; + r = (r - ml.startRow - (u32)(frameIndex * ml.dRow)); + c = (c - ml.startCol - (u32)(frameIndex * ml.dCol)); + + if (ml.wrapCoordinates == true) { + r %= anim->width; + c %= anim->height; + } return ml.callback(anim, frameIndex, pixel, r, c, ml.priv); } struct MoveRotate { - float radius; - float theta; PixelRenderer callback; void* priv; + + float radius; + float theta; }; ARGB @@ -289,6 +388,31 @@ MoveRotate(const Animation* anim, u32 frameIndex, ARGB pixel, u32 r, u32 c, void return mr.callback(anim, frameIndex, pixel, r, c, mr.priv); } +struct Interpolate { + PixelRenderer callback; + void* priv; + + u32 startFrame; + u32 endFrame; + void* value; + void* initialState; + void* finalState; + void (*interpolate)(void* value, const void* initialState, const void* finalState, double percentage); +}; + +ARGB +Interpolate(const Animation* anim, u32 frameIndex, ARGB pixel, u32 r, u32 c, void* priv) { + struct Interpolate in = *(struct Interpolate*)priv; + + double perc = (double)(frameIndex - in.startFrame) / (in.endFrame - in.startFrame); + if (perc < 0.0) perc = 0.0; + if (perc > 1.0) perc = 1.0; + + in.interpolate(in.value, in.initialState, in.finalState, perc); + + return in.callback(anim, frameIndex, pixel, r, c, in.priv); +} + int main() { Animation anim = Animation_new(512, 512, 120); @@ -300,12 +424,52 @@ main() { }; struct MoveLinear ml1 = { .startRow = 0, .startCol = 0, - .dRow = 1, .dCol = 1, + .dRow = -1, .dCol = -1, .callback = CheckerPattern, .priv = &chp1, }; Animation_pushEvent(&anim, 0, 120, MoveLinear, &ml1); + // Image + FILE* fbclc = fopen("bclc.ppm", "r"); + struct Image bclc; + struct MoveLinear ml10; + if (fbclc != NULL) { + bclc.img = ppm_read(fbclc); + bclc.noRepeat = true; + bclc.zoom = 0.4; + + ml10 = (struct MoveLinear){ + .startRow = 0, .startCol = 0, + .dRow = 0.5, .dCol = 0.5, + + .callback = Image, .priv = &bclc, + }; + Animation_pushEvent(&anim, 0, 100, MoveLinear, &ml10); + } + + // Line + struct Line l1start = { + .a = -8, .b = 6, .c = -128, + .width = 0.5, + .color = 0xFF50F0F5 + }; + struct Line l1end = { + .a = 8, .b = -4, .c = 0, + .width = 10, + .color = 0xFFF550F0 + }; + struct Line l1; + + struct Interpolate i1 = { + .startFrame = 0, .endFrame = 120, + .value = &l1, .initialState = &l1start, .finalState = &l1end, + .interpolate = LineInterpolate, + + .callback = Line, .priv = &l1, + }; + Animation_pushEvent(&anim, 0, 120, Interpolate, &i1); + // Yellow square struct SquareSettings s1 = { .width = 20, .height = 20, .color = 0xFFFFFF00, @@ -323,21 +487,36 @@ main() { }; Animation_pushEvent(&anim, 0, 80, MoveLinear, &ml3); + // Yellow square + struct SquareSettings s4 = { + .width = 20, .height = 20, .color = 0xFFFFFFFF, + }; + struct MoveLinear ml4 = { + .startRow = 40, .startCol = 40, + .dRow = 2, .dCol = 2, + .wrapCoordinates = true, + + .callback = Square, .priv = &s4, + }; + Animation_pushEvent(&anim, 0, 80, MoveLinear, &ml4); + // Green square struct SquareSettings s2 = { - .width = 25, .height = 40, .color = 0xA009FAA5, + .width = 35, .height = 50, .color = 0xA009FAA5, }; struct MoveLinear ml2 = { .startRow = 20, .startCol = 5, - .dRow = -4, .dCol = -10, + .dRow = -4, .dCol = -2, + .wrapCoordinates = true, .callback = Square, .priv = &s2, }; - Animation_pushEvent(&anim, 60, 100, MoveLinear, &ml2); - + Animation_pushEvent(&anim, 30, 110, MoveLinear, &ml2); Animation_render(&anim); + fclose(fbclc); + RGBImage_delete(&bclc.img); Animation_delete(&anim); return 0; |
