all repos — mgba @ 43e2a6ab5d32d1a1165bc37699bcf9dfc6e1992e

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