all repos — mgba @ 8af2172782b283a07ac40fcc36186c2f52bb1f7b

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	encoder->delayMs = -1;
 23}
 24
 25void ImageMagickGIFEncoderSetParams(struct ImageMagickGIFEncoder* encoder, int frameskip, int delayMs) {
 26	if (ImageMagickGIFEncoderIsOpen(encoder)) {
 27		return;
 28	}
 29	encoder->frameskip = frameskip;
 30	encoder->delayMs = delayMs;
 31}
 32
 33bool ImageMagickGIFEncoderOpen(struct ImageMagickGIFEncoder* encoder, const char* outfile) {
 34	MagickWandGenesis();
 35	encoder->wand = NewMagickWand();
 36	MagickSetImageFormat(encoder->wand, "GIF");
 37	MagickSetImageDispose(encoder->wand, PreviousDispose);
 38	encoder->outfile = strdup(outfile);
 39	encoder->frame = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
 40	encoder->currentFrame = 0;
 41	return true;
 42}
 43
 44bool ImageMagickGIFEncoderClose(struct ImageMagickGIFEncoder* encoder) {
 45	if (!encoder->wand) {
 46		return false;
 47	}
 48
 49	MagickBooleanType success = MagickWriteImages(encoder->wand, encoder->outfile, MagickTrue);
 50	DestroyMagickWand(encoder->wand);
 51	encoder->wand = 0;
 52	free(encoder->outfile);
 53	free(encoder->frame);
 54	MagickWandTerminus();
 55	return success == MagickTrue;
 56}
 57
 58bool ImageMagickGIFEncoderIsOpen(struct ImageMagickGIFEncoder* encoder) {
 59	return !!encoder->wand;
 60}
 61
 62static void _magickPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
 63	struct ImageMagickGIFEncoder* encoder = (struct ImageMagickGIFEncoder*) stream;
 64
 65	if (encoder->currentFrame % (encoder->frameskip + 1)) {
 66		++encoder->currentFrame;
 67		return;
 68	}
 69
 70	const uint8_t* pixels;
 71	unsigned stride;
 72	renderer->getPixels(renderer, &stride, (const void**) &pixels);
 73	size_t row;
 74	for (row = 0; row < VIDEO_VERTICAL_PIXELS; ++row) {
 75		memcpy(&encoder->frame[row * VIDEO_HORIZONTAL_PIXELS], &pixels[row * 4 * stride], VIDEO_HORIZONTAL_PIXELS * 4);
 76	}
 77
 78	MagickConstituteImage(encoder->wand, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, "RGBP", CharPixel, encoder->frame);
 79	uint64_t ts = encoder->currentFrame;
 80	uint64_t nts = encoder->currentFrame + encoder->frameskip + 1;
 81	if (encoder->delayMs >= 0) {
 82		ts *= encoder->delayMs;
 83		nts *= encoder->delayMs;
 84		ts /= 10;
 85		nts /= 10;
 86	} else {
 87		ts *= VIDEO_TOTAL_LENGTH * 100;
 88		nts *= VIDEO_TOTAL_LENGTH * 100;
 89		ts /= GBA_ARM7TDMI_FREQUENCY;
 90		nts /= GBA_ARM7TDMI_FREQUENCY;
 91	}
 92	MagickSetImageDelay(encoder->wand, nts - ts);
 93	++encoder->currentFrame;
 94}
 95
 96static void _magickPostAudioFrame(struct GBAAVStream* stream, int16_t left, int16_t right) {
 97	UNUSED(stream);
 98	UNUSED(left);
 99	UNUSED(right);
100	// This is a video-only format...
101}