#include #include #include #include inline int OK(int x) { return x == 0; } typedef uint8_t bool; #define false 0 #define true 1 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, const 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//// ARGB MovingBackground(const Animation* anim, u32 frameIndex, const ARGB* currentPixel, u32 r, u32 c, void* priv) { ARGB pixel; r = (r + frameIndex) / 32; c = (c + frameIndex) / 32; 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, const ARGB* currentPixel, u32 r, u32 c, void* priv) { ARGB pixel; memcpy(&pixel, currentPixel, sizeof(ARGB)); struct SquareSettings* set = (struct SquareSettings*)priv; r = (r - frameIndex * 8) % anim->width; c = (c - frameIndex * 8) % anim->height; if (0 <= r && r <= set->width && 0 <= c && c <= set->height) { ARGB_set(&pixel, set->color); } return pixel; } int main() { Animation anim = Animation_new(512, 512, 120); Animation_pushEvent(&anim, 0, 120, MovingBackground, NULL); struct SquareSettings s1 = { .width = 20, .height = 20, .color = 0xFFFFFF00, }; Animation_pushEvent(&anim, 0, 30, Square, &s1); struct SquareSettings s2 = { .width = 10, .height = 25, .color = 0x5009FAA5, }; Animation_pushEvent(&anim, 60, 100, Square, &s2); Animation_render(&anim); Animation_delete(&anim); return 0; }