computerscare-vcv-modules

computerscare modules for VCV Rack
Log | Files | Refs

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:
Msrc/ComputerscareBlank.cpp | 92++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Asrc/animatedGif.hpp | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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