all repos — mgba @ aac1add766e8bacaa27af86b710b00cd5f67c358

mGBA Game Boy Advance Emulator

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}