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