-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
195 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
# Generated by roxygen2: do not edit by hand | ||
|
||
export(gifski) | ||
export(gifski_encoder_init) | ||
export(save_gif) | ||
useDynLib(gifski,R_gifski_encoder_add_png) | ||
useDynLib(gifski,R_gifski_encoder_finalize) | ||
useDynLib(gifski,R_gifski_encoder_new) | ||
useDynLib(gifski,R_png_to_gif) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#' Streaming Gifski Encoder | ||
#' | ||
#' Starts a stateful gif encoder and returns a closure to add png frames | ||
#' to the gif. Upon completion, the caller has to invoke the closure one | ||
#' more time with `png_file = NULL` to finalize the output. | ||
#' | ||
#' @export | ||
#' @inheritParams gifski | ||
#' @family gifski | ||
#' @name streaming | ||
#' @useDynLib gifski R_gifski_encoder_new | ||
gifski_encoder_init <- function(gif_file = "animation.gif", width = 800, height = 600, loop = TRUE, delay = 1){ | ||
gif_file <- normalizePath(gif_file, mustWork = FALSE) | ||
if(!file.exists(dirname(gif_file))) | ||
stop("Target directory does not exist:", dirname(gif_file)) | ||
width <- as.integer(width) | ||
height <- as.integer(height) | ||
delay <- as.integer(delay * 100) | ||
loop <- as.logical(loop) | ||
ptr <- .Call(R_gifski_encoder_new, enc2utf8(gif_file), width, height, loop) | ||
function(png_file){ | ||
if(is.character(png_file)){ | ||
gifski_add_png(ptr, png_file, delay) | ||
} else if(is.null(png_file)){ | ||
gifski_finalize(ptr) | ||
} else { | ||
stop("Invalid input: png_file must be a file path or NULL") | ||
} | ||
} | ||
} | ||
|
||
#' @useDynLib gifski R_gifski_encoder_add_png | ||
gifski_add_png <- function(ptr, png_file, delay){ | ||
png_file <- normalizePath(png_file, mustWork = TRUE) | ||
.Call(R_gifski_encoder_add_png, ptr, enc2utf8(png_file), delay) | ||
} | ||
|
||
#' @useDynLib gifski R_gifski_encoder_finalize | ||
gifski_finalize <- function(ptr){ | ||
.Call(R_gifski_encoder_finalize, ptr) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#define R_NO_REMAP | ||
#define STRICT_R_HEADERS | ||
#include <Rinternals.h> | ||
#include <R_ext/Rdynload.h> | ||
#include <R_ext/Visibility.h> | ||
|
||
extern SEXP R_gifski_encoder_add_png(SEXP, SEXP); | ||
extern SEXP R_gifski_encoder_finalize(SEXP); | ||
extern SEXP R_gifski_encoder_new(SEXP, SEXP, SEXP, SEXP); | ||
extern SEXP R_png_to_gif(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); | ||
|
||
static const R_CallMethodDef CallEntries[] = { | ||
{"R_gifski_encoder_add_png", (DL_FUNC) &R_gifski_encoder_add_png, 2}, | ||
{"R_gifski_encoder_finalize", (DL_FUNC) &R_gifski_encoder_finalize, 1}, | ||
{"R_gifski_encoder_new", (DL_FUNC) &R_gifski_encoder_new, 4}, | ||
{"R_png_to_gif", (DL_FUNC) &R_png_to_gif, 7}, | ||
{NULL, NULL, 0} | ||
}; | ||
|
||
attribute_visible void R_init_gifski(DllInfo *dll) { | ||
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); | ||
R_useDynamicSymbols(dll, FALSE); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
#define R_NO_REMAP | ||
#define STRICT_R_HEADERS | ||
#include <Rinternals.h> | ||
|
||
// Import C headers for rust API | ||
#include <pthread.h> | ||
#include <string.h> | ||
#include "myrustlib/gifski.h" | ||
|
||
/* data to pass to encoder thread */ | ||
typedef struct { | ||
int i; | ||
char *path; | ||
GifskiError err; | ||
gifski *g; | ||
GifskiSettings settings; | ||
pthread_t encoder_thread; | ||
} gifski_ptr_info; | ||
|
||
/* gifski_write() blocks until main thread calls gifski_end_adding_frames() */ | ||
static void * gifski_encoder_thread(void * data){ | ||
gifski_ptr_info * info = data; | ||
info->err = gifski_write(info->g, info->path); | ||
return NULL; | ||
} | ||
|
||
static void fin_gifski_encoder(SEXP ptr){ | ||
gifski_ptr_info *info = (gifski_ptr_info*) R_ExternalPtrAddr(ptr); | ||
if(info == NULL) | ||
return; | ||
R_SetExternalPtrAddr(ptr, NULL); | ||
if(info->encoder_thread) | ||
pthread_cancel(info->encoder_thread); | ||
gifski_drop(info->g); | ||
free(info->path); | ||
free(info); | ||
} | ||
|
||
static gifski_ptr_info *get_info(SEXP ptr){ | ||
if(TYPEOF(ptr) != EXTPTRSXP || !Rf_inherits(ptr, "gifski_encoder")) | ||
Rf_error("pointer is not a gifski_encoder()"); | ||
if(!R_ExternalPtrAddr(ptr)) | ||
Rf_error("pointer is dead"); | ||
return R_ExternalPtrAddr(ptr); | ||
} | ||
|
||
SEXP R_gifski_encoder_new(SEXP gif_file, SEXP width, SEXP height, SEXP loop){ | ||
gifski_ptr_info *info = malloc(sizeof(gifski_ptr_info)); | ||
info->path = strdup(CHAR(STRING_ELT(gif_file, 0))); | ||
info->settings.height = Rf_asInteger(height); | ||
info->settings.width = Rf_asInteger(width); | ||
info->settings.quality = 100; | ||
info->settings.fast = false; | ||
info->settings.once = !Rf_asLogical(loop); | ||
info->err = GIFSKI_OK; | ||
info->i = 0; | ||
info->g = gifski_new(&info->settings); | ||
if(pthread_create(&info->encoder_thread, NULL, gifski_encoder_thread, info)) | ||
Rf_error("Failed to create encoder thread"); | ||
SEXP ptr = R_MakeExternalPtr(info, R_NilValue, R_NilValue); | ||
Rf_setAttrib(ptr, R_ClassSymbol, Rf_mkString("gifski_encoder")); | ||
R_RegisterCFinalizerEx(ptr, fin_gifski_encoder, TRUE); | ||
return ptr; | ||
} | ||
|
||
SEXP R_gifski_encoder_add_png(SEXP ptr, SEXP png_file, SEXP delay){ | ||
gifski_ptr_info *info = get_info(ptr); | ||
if(info->err != GIFSKI_OK) | ||
Rf_error("Gifski encoder is in bad state"); | ||
const char *path = CHAR(STRING_ELT(png_file, 0)); | ||
int d = Rf_asInteger(delay); | ||
if(gifski_add_frame_png_file(info->g, info->i, path, d) != GIFSKI_OK) | ||
Rf_error("Failed to add frame %d (%s)", info->i, path); | ||
info->i++; | ||
return Rf_ScalarInteger(info->i); | ||
} | ||
|
||
SEXP R_gifski_encoder_finalize(SEXP ptr){ | ||
gifski_ptr_info *info = get_info(ptr); | ||
if(info->err != GIFSKI_OK) | ||
Rf_error("Gifski encoder is in bad state"); | ||
if(gifski_end_adding_frames(info->g) != GIFSKI_OK) | ||
Rf_error("Failed to finalizer encoder"); | ||
pthread_join(info->encoder_thread, NULL); | ||
info->encoder_thread = NULL; | ||
SEXP path = Rf_mkString(info->path); | ||
fin_gifski_encoder(ptr); | ||
return path; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters