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