all repos — mgba @ 02020369d13abe9f332564a8b8a730770ac64849

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 GBAAVStream*, struct GBAVideoRenderer* renderer);
12static void _magickPostAudioFrame(struct GBAAVStream*, int16_t left, int16_t right);
13
14void ImageMagickGIFEncoderInit(struct ImageMagickGIFEncoder* encoder) {
15	encoder->wand = 0;
16
17	encoder->d.postVideoFrame = _magickPostVideoFrame;
18	encoder->d.postAudioFrame = _magickPostAudioFrame;
19	encoder->d.postAudioBuffer = 0;
20
21	encoder->frameskip = 2;
22}
23
24bool ImageMagickGIFEncoderOpen(struct ImageMagickGIFEncoder* encoder, const char* outfile) {
25	MagickWandGenesis();
26	encoder->wand = NewMagickWand();
27	encoder->outfile = strdup(outfile);
28	encoder->frame = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
29	encoder->currentFrame = 0;
30	return true;
31}
32void ImageMagickGIFEncoderClose(struct ImageMagickGIFEncoder* encoder) {
33	if (!encoder->wand) {
34		return;
35	}
36	MagickWriteImages(encoder->wand, encoder->outfile, MagickTrue);
37	free(encoder->outfile);
38	free(encoder->frame);
39	DestroyMagickWand(encoder->wand);
40	encoder->wand = 0;
41	MagickWandTerminus();
42}
43
44bool ImageMagickGIFEncoderIsOpen(struct ImageMagickGIFEncoder* encoder) {
45	return !!encoder->wand;
46}
47
48static void _magickPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
49	struct ImageMagickGIFEncoder* encoder = (struct ImageMagickGIFEncoder*) stream;
50
51	if (encoder->currentFrame % (encoder->frameskip + 1)) {
52		++encoder->currentFrame;
53		return;
54	}
55
56	const uint8_t* pixels;
57	unsigned stride;
58	renderer->getPixels(renderer, &stride, (const void**) &pixels);
59	size_t row;
60	for (row = 0; row < VIDEO_VERTICAL_PIXELS; ++row) {
61		memcpy(&encoder->frame[row * VIDEO_HORIZONTAL_PIXELS], &pixels[row * 4 * stride], VIDEO_HORIZONTAL_PIXELS * 4);
62	}
63
64	MagickConstituteImage(encoder->wand, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, "RGBP", CharPixel, encoder->frame);
65	uint64_t ts = encoder->currentFrame;
66	uint64_t nts = encoder->currentFrame + encoder->frameskip + 1;
67	ts *= VIDEO_TOTAL_LENGTH * 100;
68	nts *= VIDEO_TOTAL_LENGTH * 100;
69	ts /= GBA_ARM7TDMI_FREQUENCY;
70	nts /= GBA_ARM7TDMI_FREQUENCY;
71	MagickSetImageDelay(encoder->wand, nts - ts);
72	++encoder->currentFrame;
73}
74
75static void _magickPostAudioFrame(struct GBAAVStream* stream, int16_t left, int16_t right) {
76	UNUSED(stream);
77	UNUSED(left);
78	UNUSED(right);
79	// This is a video-only format...
80}