commit 9246129830cd9d49be5d862280ef4aeabf667d7a
parent af592dfd00f6ac5327a45d7e6b283bec9a6814ac
Author: Adam M <aemalone@gmail.com>
Date: Fri, 25 Dec 2020 12:55:58 -0600
Merge branch 'customblank-gif' into v1.4
Diffstat:
2 files changed, 262 insertions(+), 12 deletions(-)
diff --git a/src/ComputerscareBlank.cpp b/src/ComputerscareBlank.cpp
@@ -1,5 +1,6 @@
#include "Computerscare.hpp"
#include "ComputerscareResizableHandle.hpp"
+#include "animatedGif.hpp"
#include <osdialog.h>
#include <iostream>
#include <fstream>
@@ -13,6 +14,8 @@ struct ComputerscareBlank : Module {
bool loadedJSON = false;
std::string path;
std::string lastPath;
+
+ std::vector<std::string> paths;
float width = 120;
float height = 380;
int rotation = 0;
@@ -22,12 +25,22 @@ struct ComputerscareBlank : Module {
float xOffset = 0.f;
float yOffset = 0.f;
int imageFitEnum = 0;
+ int currentFrame = 0;
+ int numFrames = 0;
+ int stepCounter = 0;
+ float frameDelay=.5;
+ int samplesDelay=10000;
+ int speed = 100000;
+
ComputerscareSVGPanel* panelRef;
enum ParamIds {
+ ANIMATION_SPEED,
NUM_PARAMS
};
enum InputIds {
- NUM_INPUTS
+ NUM_INPUTS,
+ CLOCK_INPUT,
+ RESET_INPUT
};
enum OutputIds {
NUM_OUTPUTS
@@ -39,16 +52,32 @@ struct ComputerscareBlank : Module {
ComputerscareBlank() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
+ configParam(ANIMATION_SPEED, 0.f, 2.f, 1.f, "Animation Speed");
+
+ paths.push_back("");
+ }
+ void process(const ProcessArgs &args) override {
+ stepCounter++;
+ samplesDelay = frameDelay * args.sampleRate;
+
+ if (stepCounter > samplesDelay) {
+ //DEBUG("samplesDelay: %i",samplesDelay);
+ //DEBUG("%f",args.sampleRate);
+ stepCounter = 0;
+ if(numFrames > 1) {
+ currentFrame ++;
+ currentFrame %= numFrames;
+ }
+ }
}
- void process(const ProcessArgs &args) override {}
void onReset() override {
zoomX = 1;
zoomY = 1;
xOffset = 0;
yOffset = 0;
}
- void loadImageDialog() {
- std::string dir = this->path.empty() ? asset::user("../") : rack::string::directory(this->path);
+ void loadImageDialog(int index = 0) {
+ std::string dir = this->paths[index].empty() ? asset::user("../") : rack::string::directory(this->paths[index]);
char* pathC = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, NULL);
if (!pathC) {
return;
@@ -59,15 +88,36 @@ struct ComputerscareBlank : Module {
setPath(path);
}
- void setPath(std::string path) {
- if (path == "")
- return;
- this->path = path;
+ void setPath(std::string path, int index = 0) {
+ //if (paths.size() <= index) {
+ //paths.push_back(path);
+ //}
+ //else {
+ paths[index] = path;
+ //}
+ printf("setted %s\n", path.c_str());
+ //numFrames = paths.size();
+ currentFrame = 0;
+ }
+ void setFrameCount(int frameCount) {
+ DEBUG("setting frame count %i",frameCount);
+ numFrames=frameCount;
+ }
+ void setFrameDelay(float frameDelaySeconds) {
+ DEBUG("setting frame delay %f",frameDelaySeconds);
+ frameDelay=frameDelaySeconds;
+ }
+ std::string getPath() {
+ //return numFrames > 0 ? paths[currentFrame] : "";
+ return paths[0];
}
json_t *dataToJson() override {
json_t *rootJ = json_object();
- json_object_set_new(rootJ, "path", json_string(path.c_str()));
+ if (paths.size() > 0) {
+ json_object_set_new(rootJ, "path", json_string(paths[0].c_str()));
+ }
+
json_object_set_new(rootJ, "width", json_real(width));
json_object_set_new(rootJ, "imageFitEnum", json_integer(imageFitEnum));
json_object_set_new(rootJ, "invertY", json_boolean(invertY));
@@ -80,11 +130,16 @@ struct ComputerscareBlank : Module {
}
void dataFromJson(json_t *rootJ) override {
+
+
+
json_t *pathJ = json_object_get(rootJ, "path");
if (pathJ) {
+ //paths.push_back(path)
path = json_string_value(pathJ);
setPath(path);
}
+
json_t *widthJ = json_object_get(rootJ, "width");
if (widthJ)
width = json_number_value(widthJ);
@@ -154,6 +209,8 @@ struct PNGDisplay : TransparentWidget {
int lastEnum = -1;
std::string path = "empty";
int img = 0;
+ int currentFrame=-1;
+ AnimatedGifBuddy gifBuddy;
PNGDisplay() {
}
@@ -186,15 +243,22 @@ struct PNGDisplay : TransparentWidget {
}
void draw(const DrawArgs &args) override {
if (blankModule && blankModule->loadedJSON) {
- if (path != blankModule->path) {
- img = nvgCreateImage(args.vg, blankModule->path.c_str(), 0);
+ std::string modulePath = blankModule->getPath();
+ //printf("%s\n", modulePath.c_str());
+ if (path != modulePath) {
+ //img = nvgCreateImage(args.vg, modulePath.c_str(), 0);
+ gifBuddy = AnimatedGifBuddy(args.vg, modulePath.c_str());
+ img = gifBuddy.getHandle();
+ blankModule->setFrameCount(gifBuddy.getFrameCount());
+ blankModule->setFrameDelay(gifBuddy.getSecondsDelay());
+
nvgImageSize(args.vg, img, &imgWidth, &imgHeight);
imgRatio = ((float)imgWidth / (float)imgHeight);
if (path != "empty") {
setZooms();
}
- path = blankModule->path;
+ path = modulePath;
}
if (blankModule->imageFitEnum != lastEnum && lastEnum != -1) {
@@ -212,6 +276,10 @@ struct PNGDisplay : TransparentWidget {
nvgFill(args.vg);
nvgClosePath(args.vg);
}
+ if(blankModule->currentFrame != currentFrame) {
+ currentFrame = blankModule->currentFrame;
+ gifBuddy.displayGifFrame(args.vg,currentFrame);
+ }
}
}
};
diff --git a/src/animatedGif.hpp b/src/animatedGif.hpp
@@ -0,0 +1,181 @@
+#define STB_IMAGE_IMPLEMENTATION
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#define STBI_ONLY_PNG
+#define STBI_ONLY_JPEG
+#define STBI_ONLY_BMP
+#define STBI_ONLY_GIF
+
+#include "stb_image.h"
+#include "nanovg.h"
+#include "dtpulse.hpp"
+//#include "stb_image_write.h"
+
+/*+
+credit goes to urraka for this code:
+https://gist.github.com/urraka/685d9a6340b26b830d49
+*/
+
+
+typedef struct gif_result_t {
+ int delay;
+ unsigned char *data;
+ struct gif_result_t *next;
+} gif_result;
+
+
+
+STBIDEF unsigned char *stbi_xload(char const *filename, int *x, int *y, int *frames, std::vector<unsigned char*> &framePointers,int &frameDelay)
+{
+ FILE *f;
+ stbi__context s;
+ unsigned char *result = 0;
+
+ if (!(f = stbi__fopen(filename, "rb")))
+ return stbi__errpuc("can't fopen", "Unable to open file");
+
+ stbi__start_file(&s, f);
+
+ if (stbi__gif_test(&s))
+ {
+ int c;
+ stbi__gif g;
+ gif_result head;
+ gif_result *prev = 0, *gr = &head;
+
+ memset(&g, 0, sizeof(g));
+ memset(&head, 0, sizeof(head));
+ printf("%i\n",g);
+
+ *frames = 0;
+
+ while ((gr->data = stbi__gif_load_next(&s, &g, &c, 4)))
+ {
+
+ if (gr->data == (unsigned char*)&s)
+ {
+ gr->data = 0;
+ break;
+ }
+
+ if (prev) prev->next = gr;
+ gr->delay = g.delay;
+ frameDelay=g.delay;
+ prev = gr;
+ gr = (gif_result*) stbi__malloc(sizeof(gif_result));
+ memset(gr, 0, sizeof(gif_result));
+ //printf("loading gif frame %i, delay:%i/100s\n", *frames, g.delay);
+ //printf("gr:%i, size:%i\n", gr, sizeof(gif_result));
+ ++(*frames);
+ }
+
+ STBI_FREE(g.out);
+
+ if (gr != &head)
+ STBI_FREE(gr);
+
+ if (*frames > 0)
+ {
+ *x = g.w;
+ *y = g.h;
+ }
+
+ result = head.data;
+
+ if (*frames > 1)
+ {
+ unsigned int size = 4 * g.w * g.h;
+ unsigned char *p = 0;
+ printf("malloc amount %i\n", *frames * (size + 2));
+
+ result = (unsigned char*)stbi__malloc(*frames * (size + 2));
+ printf("result:%i, frames:%i, size:%i\n", result, *frames, size);
+ gr = &head;
+ p = result;
+ int counter = 0;
+ while (gr)
+ {
+ prev = gr;
+ //printf("p:%i, &p:%i, *p:%i\n", p, &p, *p);
+ framePointers.push_back(p);
+ memcpy(p, gr->data, size);
+ p += size;
+ *p++ = gr->delay & 0xFF;
+ *p++ = (gr->delay & 0xFF00) >> 8;
+ gr = gr->next;
+ counter++;
+ STBI_FREE(prev->data);
+ if (prev != &head) STBI_FREE(prev);
+ }
+ }
+ printf("first frame address p:%i\n",framePointers[0]);
+ printf("second frame address p:%i\n",framePointers[1]);
+ }
+ else
+ {
+ printf("NOT A GIF\n");
+ result = stbi__load_main(&s, x, y, frames, 4);
+ *frames = !!result;
+ }
+
+ fclose(f);
+ return result;
+}
+
+
+
+
+
+struct AnimatedGifBuddy {
+ std::vector<unsigned char*> framePointers;
+ int imageHandle;
+ bool initialized = false;
+ int numFrames = -1;
+ int frameDelay = 0;
+ AnimatedGifBuddy() {
+
+ }
+ AnimatedGifBuddy(NVGcontext* ctx, const char* filename) {
+ imageHandle = animatedGifCreateImage(ctx, filename, 0);
+ }
+ int getHandle() {
+ printf("imageHandle:%i\n",imageHandle);
+ return imageHandle;
+ }
+ int animatedGifCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) {
+ int w, h, n, image;
+ unsigned char* img;
+ int frame = 0;
+ stbi_set_unpremultiply_on_load(1);
+ stbi_convert_iphone_png_to_rgb(1);
+ //img = stbi_load(filename, &w, &h, &n, 4);
+ framePointers = {};
+ img = stbi_xload(filename, &w, &h, &frame, framePointers,frameDelay);
+ printf(filename);
+ printf("\nframe delay:%i\n",frameDelay);
+ printf("loaded %i frames\n", framePointers.size());
+ numFrames = (int) framePointers.size();
+ //printVector(framePointers);
+ if (img == NULL) {
+// printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
+ return 0;
+ }
+ image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img);
+ //stbi_image_free(img);
+
+ initialized = true;
+ return image;
+ }
+ void displayGifFrame(NVGcontext* ctx, int frameNumber) {
+ if (initialized && frameNumber < numFrames) {
+ const unsigned char* dataAtFrame = framePointers[frameNumber];
+ //printf("displaying frame %i\n",frameNumber);
+ nvgUpdateImage(ctx, imageHandle, dataAtFrame);
+ }
+ }
+ int getFrameCount() {
+ return numFrames;
+ }
+ float getSecondsDelay() {
+ return ((float) frameDelay)/100;
+ }
+};
+\ No newline at end of file