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