#include #include #include #include using namespace std; using namespace Magick; void *memset16(void *m, uint16_t val, size_t count) { uint16_t *buf = (uint16_t *)m; while (count--) *buf++ = val; return m; } Napi::Value Speed(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); try { Napi::Object obj = info[0].As(); Napi::Buffer data = obj.Get("data").As>(); bool slow = obj.Has("slow") ? obj.Get("slow").As().Value() : false; string type = obj.Get("type").As().Utf8Value(); int delay = obj.Has("delay") ? obj.Get("delay").As().Int32Value() : 0; int speed = obj.Has("speed") ? obj.Get("speed").As().Int32Value() : 2; Napi::Object result = Napi::Object::New(env); char *fileData = data.Data(); char *match = (char *)"\x00\x21\xF9\x04"; // if passed a delay, use that. otherwise iterate over every frame. if (delay == 0) { vector old_delays; bool removeFrames = false; char *lastPos; int amount = 0; lastPos = (char *)memchr(fileData, '\x00', data.Length()); while (lastPos != NULL) { if (memcmp(lastPos, match, 4) != 0) { lastPos = (char *)memchr(lastPos + 1, '\x00', (data.Length() - (lastPos - fileData)) - 1); continue; } ++amount; uint16_t old_delay; memcpy(&old_delay, lastPos + 5, 2); old_delays.push_back(old_delay); lastPos = (char *)memchr(lastPos + 1, '\x00', (data.Length() - (lastPos - fileData)) - 1); } int currentFrame = 0; lastPos = (char *)memchr(fileData, '\x00', data.Length()); while (lastPos != NULL) { if (memcmp(lastPos, match, 4) != 0) { lastPos = (char *)memchr(lastPos + 1, '\x00', (data.Length() - (lastPos - fileData)) - 1); continue; } uint16_t new_delay = slow ? old_delays[currentFrame] * speed : old_delays[currentFrame] / speed; if (!slow && new_delay <= 1) { removeFrames = true; break; } memset16(lastPos + 5, new_delay, 1); lastPos = (char *)memchr(lastPos + 1, '\x00', (data.Length() - (lastPos - fileData)) - 1); ++currentFrame; } result.Set("data", Napi::Buffer::Copy(env, fileData, data.Length())); if (removeFrames) { Blob blob; list frames; try { readImages(&frames, Blob(data.Data(), data.Length())); } catch (Magick::WarningCoder &warning) { cerr << "Coder Warning: " << warning.what() << endl; } catch (Magick::Warning &warning) { cerr << "Warning: " << warning.what() << endl; } for (list::iterator i = frames.begin(); i != frames.end(); ++i) { int index = distance(frames.begin(), i); i->animationDelay(old_delays[index]); } for (int i = 0; i < speed - 1; ++i) { auto it = frames.begin(); while(it != frames.end() && ++it != frames.end()) it = frames.erase(it); } for_each(frames.begin(), frames.end(), magickImage(type)); writeImages(frames.begin(), frames.end(), &blob); result.Set("data", Napi::Buffer::Copy(env, (char *)blob.data(), blob.length())); } } else { char *lastPos; bool removeFrames = false; lastPos = (char *)memchr(fileData, '\x00', data.Length()); while (lastPos != NULL) { if (memcmp(lastPos, match, 4) != 0) { lastPos = (char *)memchr(lastPos + 1, '\x00', (data.Length() - (lastPos - fileData)) - 1); continue; } uint16_t old_delay; memcpy(&old_delay, lastPos + 5, 2); int new_delay = slow ? delay * speed : delay / speed; if (!slow && new_delay <= 1) { removeFrames = true; } break; } if (removeFrames) { Blob blob; list frames; try { readImages(&frames, Blob(data.Data(), data.Length())); } catch (Magick::WarningCoder &warning) { cerr << "Coder Warning: " << warning.what() << endl; } catch (Magick::Warning &warning) { cerr << "Warning: " << warning.what() << endl; } for (int i = 0; i < speed - 1; ++i) { auto it = frames.begin(); while(it != frames.end() && ++it != frames.end()) it = frames.erase(it); } for_each(frames.begin(), frames.end(), magickImage(type)); writeImages(frames.begin(), frames.end(), &blob); result.Set("data", Napi::Buffer::Copy(env, (char *)blob.data(), blob.length())); } else { while (lastPos != NULL) { if (memcmp(lastPos, match, 4) != 0) { lastPos = (char *)memchr(lastPos + 1, '\x00', (data.Length() - (lastPos - fileData)) - 1); continue; } uint16_t old_delay; memcpy(&old_delay, lastPos + 5, 2); int new_delay = slow ? delay * speed : delay / speed; memset16(lastPos + 5, new_delay, 1); lastPos = (char *)memchr(lastPos + 1, '\x00', (data.Length() - (lastPos - fileData)) - 1); } } } result.Set("type", type); return result; } catch (std::exception const &err) { throw Napi::Error::New(env, err.what()); } catch (...) { throw Napi::Error::New(env, "Unknown error"); } }