summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSyndamia <kamen@syndamia.com>2026-03-28 22:14:32 +0200
committerSyndamia <kamen@syndamia.com>2026-03-28 22:14:32 +0200
commit1cc8f72fb1c44e203a324e274038c2883c351fbb (patch)
treebb3f63143fc643fa9d35bcba6566a0d6ef445cab
parent6a63e2c0e1f37c502501e1b611e8dc289476c1f2 (diff)
downloadppm_graphics-1cc8f72fb1c44e203a324e274038c2883c351fbb.tar
ppm_graphics-1cc8f72fb1c44e203a324e274038c2883c351fbb.tar.gz
ppm_graphics-1cc8f72fb1c44e203a324e274038c2883c351fbb.zip
feat: Add animation capabilities with linear interpolation
-rw-r--r--AnimationRender/AnimationRender.c26
-rw-r--r--AnimationRender/AnimationRender.h9
-rw-r--r--AnimationRender/F_Draw.c21
-rw-r--r--AnimationRender/F_Draw.h17
-rw-r--r--AnimationRender/F_Interpolate.c33
-rw-r--r--AnimationRender/F_Interpolate.h20
-rw-r--r--AnimationRender/FrameCallback.h22
-rw-r--r--global.h4
-rw-r--r--graphics.c37
9 files changed, 176 insertions, 13 deletions
diff --git a/AnimationRender/AnimationRender.c b/AnimationRender/AnimationRender.c
new file mode 100644
index 0000000..8974ba3
--- /dev/null
+++ b/AnimationRender/AnimationRender.c
@@ -0,0 +1,26 @@
+#include "AnimationRender.h"
+#include "FrameCallback.h"
+
+void RenderAnimation(u32 width, u32 height, u32 totalFrames, const FrameCallback** callbacks, usize sizeCallbacks) {
+ RGBImage frameBuffer = RGBImage_new(width, height);
+
+ struct FrameContext fc;
+ fc.startFrame = 0;
+ fc.endFrame = totalFrames;
+ fc.frameBuffer = frameBuffer;
+
+ for (u32 f = 0; f < totalFrames; ++f) {
+ fc.frame = f;
+ for (usize i = 0; i < sizeCallbacks; ++i) {
+ // Soft context reset
+ if (callbacks[i] == NULL) {
+ fc.startFrame = 0;
+ fc.endFrame = totalFrames;
+ }
+ else
+ callbacks[i]->callback(callbacks[i]->callback_self, &fc);
+ }
+ }
+
+ RGBImage_delete(&frameBuffer);
+}
diff --git a/AnimationRender/AnimationRender.h b/AnimationRender/AnimationRender.h
new file mode 100644
index 0000000..19545ee
--- /dev/null
+++ b/AnimationRender/AnimationRender.h
@@ -0,0 +1,9 @@
+#ifndef _ANIMATION_RENDER
+#define _ANIMATION_RENDER
+
+#include "../global.h"
+#include "FrameCallback.h"
+
+void RenderAnimation(u32 width, u32 height, u32 totalFrames, const FrameCallback** callbacks, usize sizeCallbacks);
+
+#endif /* _ANIMATION_RENDER */
diff --git a/AnimationRender/F_Draw.c b/AnimationRender/F_Draw.c
new file mode 100644
index 0000000..fb90883
--- /dev/null
+++ b/AnimationRender/F_Draw.c
@@ -0,0 +1,21 @@
+#include "F_Draw.h"
+#include "../FrameRender/FrameRender.h"
+#include <stdlib.h>
+#include <string.h>
+
+void Draw_callback(Draw* self, struct FrameContext* fc) {
+ if (self->startFrame <= fc->frame && fc->frame <= self->endFrame) {
+ RenderFrame(fc->frameBuffer, self->callbacks, self->sizeCallbacks);
+ ppm6_write(stdout, fc->frameBuffer);
+ }
+}
+
+FrameCallback* Draw_setup(Draw obj) {
+ FrameCallback* fca = malloc(sizeof(struct FrameCallback));
+
+ fca->callback = (FrameCallback_callback)Draw_callback;
+ fca->callback_self = malloc(sizeof(obj));
+ memcpy(fca->callback_self, &obj, sizeof(obj));
+
+ return fca;
+}
diff --git a/AnimationRender/F_Draw.h b/AnimationRender/F_Draw.h
new file mode 100644
index 0000000..729cec9
--- /dev/null
+++ b/AnimationRender/F_Draw.h
@@ -0,0 +1,17 @@
+#ifndef F_DRAW
+#define F_DRAW
+
+#include "../global.h"
+#include "../FrameRender/PixelCallback.h"
+#include "FrameCallback.h"
+
+typedef struct Draw {
+ u32 startFrame;
+ u32 endFrame;
+ const PixelCallback** callbacks;
+ usize sizeCallbacks;
+} Draw;
+
+FrameCallback* Draw_setup(Draw obj);
+
+#endif /* F_DRAW */
diff --git a/AnimationRender/F_Interpolate.c b/AnimationRender/F_Interpolate.c
new file mode 100644
index 0000000..cc72163
--- /dev/null
+++ b/AnimationRender/F_Interpolate.c
@@ -0,0 +1,33 @@
+#include "F_Interpolate.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void Interpolate_callback(Interpolate* self, struct FrameContext* fc) {
+ if (self->interpolate == NULL) {
+ fprintf(stderr, "Interpolation function cannot be NULL!\n");
+ return;
+ }
+
+ double p = (double)(fc->frame - fc->startFrame) / (fc->endFrame - fc->startFrame);
+
+ if (0.0 <= p && p <= 1.0)
+ self->interpolate(self->value, self->startValue, self->endValue, p);
+}
+
+FrameCallback* Interpolate_setup(Interpolate obj) {
+ FrameCallback* fca = malloc(sizeof(struct FrameCallback));
+
+ fca->callback = (FrameCallback_callback)Interpolate_callback;
+ fca->callback_self = malloc(sizeof(obj));
+ memcpy(fca->callback_self, &obj, sizeof(obj));
+
+ return fca;
+}
+
+#define NUM_INTERPOLATE(a, b, perc) ((a) * (1.0 - (perc)) + (b) * (perc))
+
+void Interpolate_TransformCenter(TransformCenter* value, const TransformCenter* start, const TransformCenter* end, double progress) {
+ value->centerX = NUM_INTERPOLATE(start->centerX, end->centerX, progress);
+ value->centerY = NUM_INTERPOLATE(start->centerY, end->centerY, progress);
+}
diff --git a/AnimationRender/F_Interpolate.h b/AnimationRender/F_Interpolate.h
new file mode 100644
index 0000000..502fbb4
--- /dev/null
+++ b/AnimationRender/F_Interpolate.h
@@ -0,0 +1,20 @@
+#ifndef F_INTERPOLATE
+#define F_INTERPOLATE
+
+#include "FrameCallback.h"
+#include "../FrameRender/P_TransformCenter.h"
+
+typedef void (*f_interpolate)(void* value, const void* start, const void* end, double progress);
+
+typedef struct Interpolate {
+ f_interpolate interpolate;
+ void* value;
+ const void* startValue;
+ const void* endValue;
+} Interpolate;
+
+FrameCallback* Interpolate_setup(Interpolate obj);
+
+void Interpolate_TransformCenter(TransformCenter* value, const TransformCenter* start, const TransformCenter* end, double progress);
+
+#endif /* F_INTERPOLATE */
diff --git a/AnimationRender/FrameCallback.h b/AnimationRender/FrameCallback.h
new file mode 100644
index 0000000..b3292bb
--- /dev/null
+++ b/AnimationRender/FrameCallback.h
@@ -0,0 +1,22 @@
+#ifndef _FRAME_CALLBACK
+#define _FRAME_CALLBACK
+
+#include "../global.h"
+#include "../RGBImage.h"
+
+struct FrameContext {
+ u32 frame;
+ u32 startFrame;
+ u32 endFrame;
+
+ RGBImage frameBuffer;
+};
+
+typedef void (*FrameCallback_callback)(void* self, struct FrameContext* fc);
+
+typedef struct FrameCallback {
+ FrameCallback_callback callback;
+ void* callback_self;
+} FrameCallback;
+
+#endif /* _FRAME_CALLBACK */
diff --git a/global.h b/global.h
index fc31f28..e54f111 100644
--- a/global.h
+++ b/global.h
@@ -3,10 +3,6 @@
#include <stdint.h>
-typedef uint8_t bool;
-#define false 0
-#define true 1
-
typedef int32_t i32;
typedef uint32_t u32;
typedef uint32_t usize;
diff --git a/graphics.c b/graphics.c
index d5f16e4..0207ac2 100644
--- a/graphics.c
+++ b/graphics.c
@@ -2,6 +2,9 @@
* gcc -std=c99 *.c -lm && ./a.out | ffmpeg -r 30 -i - output.mp4
*/
+#include "AnimationRender/AnimationRender.h"
+#include "AnimationRender/F_Draw.h"
+#include "AnimationRender/F_Interpolate.h"
#include "FrameRender/FrameRender.h"
#include "FrameRender/P_Checker.h"
#include "FrameRender/P_Circle.h"
@@ -9,13 +12,16 @@
#include "FrameRender/P_Square.h"
#include "FrameRender/P_TransformCenter.h"
#include "FrameRender/P_TransformRotate.h"
+#include "FrameRender/PixelCallback.h"
#include "RGBImage.h"
-#include "math.h"
#include <stdlib.h>
+# define M_PI 3.14159265358979323846 /* pi */
+
int
main() {
- RGBImage frame = RGBImage_new(512, 512);
+ u32 width = 512;
+ u32 height = 512;
RGBImage bclc = ppm_read(fopen("bclc.ppm", "rb"));
@@ -24,28 +30,28 @@ main() {
.squareSize = 64, .colorA = 0xFF000000, .colorB = 0xFFFF00FF,
}),
TransformCenter_setup((TransformCenter){
- .centerX = frame.width / 2, .centerY = frame.height / 2,
+ .centerX = width / 2, .centerY = height / 2,
}),
Square_setup((Square){
.width = 100, .height = 100, .color = 0xFFFFFF00,
}),
NULL,
TransformRotate_setup((TransformRotate){
- .angle = M_PI_4 / 2,
+ .angle = M_PI / 8,
}),
Square_setup((Square){
.width = 200, .height = 200, .color = 0xFF00FFFF,
}),
NULL,
TransformCenter_setup((TransformCenter){
- .centerX = frame.width * 3/4, .centerY = frame.height / 2,
+ .centerX = width * 3/4, .centerY = height / 2,
}),
Circle_setup((Circle){
.radius = 50, .color = 0x80FFFFFF,
}),
NULL,
TransformCenter_setup((TransformCenter){
- .centerX = frame.width / 8, .centerY = frame.height * 3/5,
+ .centerX = width / 8, .centerY = height * 3/5,
}),
Image_setup((Image){
.img = bclc,
@@ -54,10 +60,23 @@ main() {
};
u32 sizeCallbacks = sizeof(callbacks) / sizeof(*callbacks);
- RenderFrame(frame, (const PixelCallback**)callbacks, sizeCallbacks);
- ppm6_write(stdout, frame);
+ TransformCenter img1 = { .centerX = width / 8, .centerY = height * 3/5 },
+ img2 = { .centerX = height * 3/5, .centerY = width / 8 };
+ const FrameCallback* frameCallbacks[] = {
+ Interpolate_setup((Interpolate){
+ .startValue = &img1, .endValue = &img2,
+ .value = callbacks[10]->callback_self,
+ .interpolate = (f_interpolate)Interpolate_TransformCenter,
+ }),
+ Draw_setup((Draw){
+ .startFrame = 0, .endFrame = 150,
+ .callbacks = (const PixelCallback**)callbacks, .sizeCallbacks = sizeCallbacks,
+ }),
+ };
+ u32 sizeFrameCallbacks = sizeof(frameCallbacks) / sizeof(*frameCallbacks);
+
+ RenderAnimation(width, height, 150, frameCallbacks, sizeFrameCallbacks);
- RGBImage_delete(&frame);
RGBImage_delete(&bclc);
for (u32 i = 0; i < sizeCallbacks; ++i) {
if (callbacks[i] == NULL) continue;