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