all repos — mgba @ 67905d281bfecbb06f51f2ca5ac939df378734a5

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