src/platform/imagemagick/imagemagick-gif-encoder.c (view raw)
1#include "imagemagick-gif-encoder.h"
2
3#include "gba-video.h"
4
5static void _magickPostVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
6static void _magickPostAudioFrame(struct GBAAVStream*, int32_t left, int32_t right);
7
8void ImageMagickGIFEncoderInit(struct ImageMagickGIFEncoder* encoder) {
9 encoder->wand = 0;
10
11 encoder->d.postVideoFrame = _magickPostVideoFrame;
12 encoder->d.postAudioFrame = _magickPostAudioFrame;
13
14 encoder->frameskip = 2;
15}
16
17bool ImageMagickGIFEncoderOpen(struct ImageMagickGIFEncoder* encoder, const char* outfile) {
18 MagickWandGenesis();
19 encoder->wand = NewMagickWand();
20 encoder->outfile = strdup(outfile);
21 encoder->frame = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
22 encoder->currentFrame = 0;
23 return true;
24}
25void ImageMagickGIFEncoderClose(struct ImageMagickGIFEncoder* encoder) {
26 if (!encoder->wand) {
27 return;
28 }
29 MagickWriteImages(encoder->wand, encoder->outfile, MagickTrue);
30 free(encoder->outfile);
31 free(encoder->frame);
32 DestroyMagickWand(encoder->wand);
33 encoder->wand = 0;
34 MagickWandTerminus();
35}
36
37bool ImageMagickGIFEncoderIsOpen(struct ImageMagickGIFEncoder* encoder) {
38 return !!encoder->wand;
39}
40
41static void _magickPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
42 struct ImageMagickGIFEncoder* encoder = (struct ImageMagickGIFEncoder*) stream;
43
44 if (encoder->currentFrame % (encoder->frameskip + 1)) {
45 ++encoder->currentFrame;
46 return;
47 }
48
49 uint8_t* pixels;
50 unsigned stride;
51 renderer->getPixels(renderer, &stride, (void**) &pixels);
52 size_t row;
53 for (row = 0; row < VIDEO_VERTICAL_PIXELS; ++row) {
54 memcpy(&encoder->frame[row * VIDEO_HORIZONTAL_PIXELS], &pixels[row * 4 *stride], VIDEO_HORIZONTAL_PIXELS * 4);
55 }
56
57 MagickConstituteImage(encoder->wand, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, "RGBP", CharPixel, encoder->frame);
58 uint64_t ts = encoder->currentFrame;
59 uint64_t nts = encoder->currentFrame + encoder->frameskip + 1;
60 ts *= VIDEO_TOTAL_LENGTH * 100;
61 nts *= VIDEO_TOTAL_LENGTH * 100;
62 ts /= GBA_ARM7TDMI_FREQUENCY;
63 nts /= GBA_ARM7TDMI_FREQUENCY;
64 MagickSetImageDelay(encoder->wand, nts - ts);
65 ++encoder->currentFrame;
66}
67
68static void _magickPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t right) {
69 UNUSED(stream);
70 UNUSED(left);
71 UNUSED(right);
72 // This is a video-only format...
73}