src/feature/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 <mgba/internal/gba/gba.h>
9#include <mgba/internal/gba/video.h>
10#include <mgba-util/string.h>
11
12static void _magickPostVideoFrame(struct mAVStream*, const color_t* pixels, size_t stride);
13static void _magickVideoDimensionsChanged(struct mAVStream*, unsigned width, unsigned height);
14static void _magickVideoFrameRateChanged(struct mAVStream*, unsigned numerator, unsigned denominator);
15
16void ImageMagickGIFEncoderInit(struct ImageMagickGIFEncoder* encoder) {
17 encoder->wand = 0;
18
19 encoder->d.videoDimensionsChanged = _magickVideoDimensionsChanged;
20 encoder->d.postVideoFrame = _magickPostVideoFrame;
21 encoder->d.postAudioFrame = 0;
22 encoder->d.postAudioBuffer = 0;
23 encoder->d.videoFrameRateChanged = _magickVideoFrameRateChanged;
24
25 encoder->frameskip = 2;
26 encoder->delayMs = -1;
27
28 encoder->iwidth = VIDEO_HORIZONTAL_PIXELS;
29 encoder->iheight = VIDEO_VERTICAL_PIXELS;
30 encoder->numerator = VIDEO_TOTAL_LENGTH;
31 encoder->denominator = GBA_ARM7TDMI_FREQUENCY;
32}
33
34void ImageMagickGIFEncoderSetParams(struct ImageMagickGIFEncoder* encoder, int frameskip, int delayMs) {
35 if (ImageMagickGIFEncoderIsOpen(encoder)) {
36 return;
37 }
38 encoder->frameskip = frameskip;
39 encoder->delayMs = delayMs;
40}
41
42bool ImageMagickGIFEncoderOpen(struct ImageMagickGIFEncoder* encoder, const char* outfile) {
43 MagickWandGenesis();
44 encoder->wand = NewMagickWand();
45 MagickSetImageFormat(encoder->wand, "GIF");
46 MagickSetImageDispose(encoder->wand, PreviousDispose);
47 encoder->outfile = strdup(outfile);
48 encoder->frame = malloc(encoder->iwidth * encoder->iheight * 4);
49 encoder->currentFrame = 0;
50 return true;
51}
52
53bool ImageMagickGIFEncoderClose(struct ImageMagickGIFEncoder* encoder) {
54 if (!encoder->wand) {
55 return false;
56 }
57
58 MagickBooleanType success = MagickWriteImages(encoder->wand, encoder->outfile, MagickTrue);
59 DestroyMagickWand(encoder->wand);
60 encoder->wand = 0;
61 free(encoder->outfile);
62 free(encoder->frame);
63 MagickWandTerminus();
64 return success == MagickTrue;
65}
66
67bool ImageMagickGIFEncoderIsOpen(struct ImageMagickGIFEncoder* encoder) {
68 return !!encoder->wand;
69}
70
71static void _magickPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size_t stride) {
72 struct ImageMagickGIFEncoder* encoder = (struct ImageMagickGIFEncoder*) stream;
73
74 if (encoder->currentFrame % (encoder->frameskip + 1)) {
75 ++encoder->currentFrame;
76 return;
77 }
78
79 const uint8_t* p8 = (const uint8_t*) pixels;
80 size_t row;
81 for (row = 0; row < encoder->iheight; ++row) {
82 memcpy(&encoder->frame[row * encoder->iwidth], &p8[row * 4 * stride], encoder->iwidth * 4);
83 }
84
85 MagickConstituteImage(encoder->wand, encoder->iwidth, encoder->iheight, "RGBP", CharPixel, encoder->frame);
86 uint64_t ts = encoder->currentFrame;
87 uint64_t nts = encoder->currentFrame + encoder->frameskip + 1;
88 if (encoder->delayMs >= 0) {
89 ts *= encoder->delayMs;
90 nts *= encoder->delayMs;
91 ts /= 10;
92 nts /= 10;
93 } else {
94 ts *= encoder->numerator * 100;
95 nts *= encoder->numerator * 100;
96 ts /= encoder->denominator;
97 nts /= encoder->denominator;
98 }
99 MagickSetImageDelay(encoder->wand, nts - ts);
100 ++encoder->currentFrame;
101}
102
103static void _magickVideoDimensionsChanged(struct mAVStream* stream, unsigned width, unsigned height) {
104 struct ImageMagickGIFEncoder* encoder = (struct ImageMagickGIFEncoder*) stream;
105 if (encoder->iwidth == width && encoder->iheight == height) {
106 return;
107 }
108 free(encoder->frame);
109 encoder->iwidth = width;
110 encoder->iheight = height;
111 encoder->frame = malloc(encoder->iwidth * encoder->iheight * 4);
112}
113
114static void _magickVideoFrameRateChanged(struct mAVStream* stream, unsigned numerator, unsigned denominator) {
115 struct ImageMagickGIFEncoder* encoder = (struct ImageMagickGIFEncoder*) stream;
116 encoder->numerator = numerator;
117 encoder->denominator = denominator;
118}