#include #include #include #include #include inline int OK(int x) { return x == 0; } typedef uint8_t bool; #define false 0 #define true 1 typedef int32_t i32; typedef uint32_t u32; ////RGBImage//// typedef uint8_t byte; typedef uint16_t byte2; typedef uint32_t byte4; typedef struct ARGB { byte a; byte r; byte g; byte b; } ARGB; void ARGB_set(ARGB* rgb, byte4 color) { rgb->a = (0xFF000000 & color) >> 24; rgb->r = (0x00FF0000 & color) >> 16; rgb->g = (0x0000FF00 & color) >> 8; rgb->b = (0x000000FF & color); } void ARGB_merge(ARGB* bottom, ARGB top) { bottom->a = 255; double perc = top.a / 255.0; bottom->r = (bottom->r * (1.0 - perc)) + (top.r * perc); bottom->g = (bottom->g * (1.0 - perc)) + (top.g * perc); bottom->b = (bottom->b * (1.0 - perc)) + (top.b * perc); } typedef struct RGBImage { u32 width; u32 height; ARGB* img; } RGBImage; RGBImage RGBImage_new(u32 width, u32 height) { return (RGBImage){ .width = width, .height = height, .img = malloc(sizeof(ARGB) * width * height) }; } void RGBImage_free(RGBImage img) { free(img.img); } void RGBImage_delete(RGBImage* img) { RGBImage_free(*img); img->width = img->height = 0; img->img = NULL; } ARGB* RGBImage_at(RGBImage img, int row, int col) { return img.img + (row % img.height) * img.width + col % img.width; } int ppm6_write(FILE* f, RGBImage img) { fprintf(f, "P6\n%d %d\n255\n", img.width, img.height); size_t size = img.width * img.height; for (size_t i = 0; i < size; ++i) { fputc(img.img[i].r, f); fputc(img.img[i].g, f); fputc(img.img[i].b, f); } return fflush(f); } ////Animation//// struct AnimationEventNode; typedef struct AnimationEventNode AnimationEventNode; typedef struct Animation { u32 width; u32 height; u32 frameCount; RGBImage frameBuffer; AnimationEventNode* events; } Animation; typedef ARGB (*PixelRenderer)(const Animation* anim, u32 currentFrame, ARGB pixel, u32 row, u32 col, void* priv); struct AnimationEventNode { AnimationEventNode* next; u32 startFrame; u32 endFrame; PixelRenderer renderCallback; void* privateData; }; AnimationEventNode* AnimationEventNode_new(u32 startFrame, u32 endFrame, PixelRenderer renderCallback, void* privateData) { AnimationEventNode* aen = (AnimationEventNode*)malloc(sizeof(AnimationEventNode)); aen->next = NULL; aen->startFrame = startFrame; aen->endFrame = endFrame; aen->renderCallback = renderCallback; aen->privateData = privateData; return aen; } Animation Animation_new(u32 width, u32 height, u32 frameCount) { return (Animation){ .width = width, .height = height, .frameCount = frameCount, .frameBuffer = RGBImage_new(width, height), .events = NULL, }; } void Animation_free(Animation anim) { RGBImage_free(anim.frameBuffer); while (anim.events != NULL) { AnimationEventNode* toFree = anim.events; anim.events = anim.events->next; free(toFree); } } void Animation_delete(Animation* anim) { Animation_free(*anim); anim->width = anim->height = anim->frameCount = 0; RGBImage_delete(&anim->frameBuffer); } void Animation_pushEvent(Animation* anim, u32 startFrame, u32 endFrame, PixelRenderer renderer, void* privateData) { if (anim->events == NULL) { anim->events = AnimationEventNode_new(startFrame, endFrame, renderer, privateData); return; } AnimationEventNode* prevNode = NULL, *currNode = anim->events; while (currNode != NULL) { if (startFrame < currNode->startFrame) { prevNode->next = AnimationEventNode_new(startFrame, endFrame, renderer, privateData); prevNode->next->next = currNode; return; } prevNode = currNode; currNode = currNode->next; } prevNode->next = AnimationEventNode_new(startFrame, endFrame, renderer, privateData); } void Animation_render(Animation* anim) { for (int currFrame = 0; currFrame < anim->frameCount; ++currFrame) { if (anim->events != NULL) for (int r = 0; r < anim->frameBuffer.width; ++r) for (int c = 0; c < anim->frameBuffer.height; ++c) { for (AnimationEventNode* aen = anim->events; aen != NULL && aen->startFrame <= currFrame; aen = aen->next) { ARGB* pixel = RGBImage_at(anim->frameBuffer, r, c); ARGB newPixel = aen->renderCallback(anim, currFrame, *pixel, r, c, aen->privateData); ARGB_merge(pixel, newPixel); } AnimationEventNode* prevNode = NULL, *currNode = anim->events; while (currNode != NULL && currNode->startFrame <= currFrame) { if (currNode->endFrame <= currFrame) { if (prevNode != NULL) prevNode->next = currNode->next; else anim->events = NULL; free(currNode); } else { prevNode = currNode; currNode = currNode->next; } } } ppm6_write(stdout, anim->frameBuffer); } } ////main//// struct CheckerPattern { u32 squareSize; byte4 colorA; byte4 colorB; }; ARGB CheckerPattern(const Animation* anim, u32 frameIndex, ARGB pixel, u32 r, u32 c, void* priv) { struct CheckerPattern chp = *(struct CheckerPattern*)priv; r /= chp.squareSize; c /= chp.squareSize; if ((r + c) % 2 == 0) { ARGB_set(&pixel, 0xFF000000); } else { ARGB_set(&pixel, 0xFFFF00FF); } return pixel; } struct SquareSettings { u32 width; u32 height; byte4 color; }; ARGB Square(const Animation* anim, u32 frameIndex, ARGB pixel, u32 r, u32 c, void* priv) { struct SquareSettings* set = (struct SquareSettings*)priv; if (0 <= r && r <= set->width && 0 <= c && c <= set->height) { ARGB_set(&pixel, set->color); } return pixel; } struct MoveLinear { u32 startRow; u32 startCol; float dRow; float dCol; PixelRenderer callback; void* priv; }; 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; return ml.callback(anim, frameIndex, pixel, r, c, ml.priv); } struct MoveRotate { float radius; float theta; PixelRenderer callback; void* priv; }; ARGB MoveRotate(const Animation* anim, u32 frameIndex, ARGB pixel, u32 r, u32 c, void* priv) { struct MoveRotate mr = *(struct MoveRotate*)priv; r = (r + (u32)(mr.radius * cos(mr.theta * frameIndex))) % anim->width; c = (c + (u32)(mr.radius * sin(mr.theta * frameIndex))) % anim->height; return mr.callback(anim, frameIndex, pixel, r, c, mr.priv); } int main() { Animation anim = Animation_new(512, 512, 120); // Background struct CheckerPattern chp1 = { .squareSize = 32, .colorA = 0xFF000000, .colorB = 0xFFFF00FF, }; struct MoveLinear ml1 = { .startRow = 0, .startCol = 0, .dRow = 1, .dCol = 1, .callback = CheckerPattern, .priv = &chp1, }; Animation_pushEvent(&anim, 0, 120, MoveLinear, &ml1); // Yellow square struct SquareSettings s1 = { .width = 20, .height = 20, .color = 0xFFFFFF00, }; struct MoveRotate mr1 = { .radius = 20, .theta = 3.14159 / 20, .callback = Square, .priv = &s1, }; struct MoveLinear ml3 = { .startRow = 0, .startCol = 0, .dRow = -3, .dCol = 3, .callback = MoveRotate, .priv = &mr1, }; Animation_pushEvent(&anim, 0, 80, MoveLinear, &ml3); // Green square struct SquareSettings s2 = { .width = 25, .height = 40, .color = 0xA009FAA5, }; struct MoveLinear ml2 = { .startRow = 20, .startCol = 5, .dRow = -4, .dCol = -10, .callback = Square, .priv = &s2, }; Animation_pushEvent(&anim, 60, 100, MoveLinear, &ml2); Animation_render(&anim); Animation_delete(&anim); return 0; }