all repos — mgba @ fe2f67e2aa2246e8ec2c2599a480d6ff7557f3f7

mGBA Game Boy Advance Emulator

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