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