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