Implemented a faster speed/slow method that doesn't require decoding the GIF
This commit is contained in:
		
							parent
							
								
									61758fa3bb
								
							
						
					
					
						commit
						e6b4db6e3e
					
				
					 1 changed files with 102 additions and 23 deletions
				
			
		
							
								
								
									
										135
									
								
								natives/speed.cc
									
										
									
									
									
								
							
							
						
						
									
										135
									
								
								natives/speed.cc
									
										
									
									
									
								
							| 
						 | 
					@ -6,6 +6,13 @@
 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
using namespace Magick;
 | 
					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::Value Speed(const Napi::CallbackInfo &info) {
 | 
				
			||||||
  Napi::Env env = info.Env();
 | 
					  Napi::Env env = info.Env();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,32 +27,64 @@ Napi::Value Speed(const Napi::CallbackInfo &info) {
 | 
				
			||||||
    int speed =
 | 
					    int speed =
 | 
				
			||||||
        obj.Has("speed") ? obj.Get("speed").As<Napi::Number>().Int32Value() : 2;
 | 
					        obj.Has("speed") ? obj.Get("speed").As<Napi::Number>().Int32Value() : 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Napi::Object result = Napi::Object::New(env);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    char *fileData = data.Data();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    char *match = "\x21\xF9\x04";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // if passed a delay, use that. otherwise iterate over every frame.
 | 
				
			||||||
 | 
					    if (delay == 0) {
 | 
				
			||||||
 | 
					      vector<uint16_t> old_delays;
 | 
				
			||||||
 | 
					      bool removeFrames = false;
 | 
				
			||||||
 | 
					      char *lastPos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      int amount = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      lastPos = (char *)memchr(fileData, '\x21', data.Length());
 | 
				
			||||||
 | 
					      while (lastPos != NULL) {
 | 
				
			||||||
 | 
					        if (memcmp(lastPos, match, 3) != 0) {
 | 
				
			||||||
 | 
					          lastPos = (char *)memchr(lastPos + 1, '\x21',
 | 
				
			||||||
 | 
					                                   (data.Length() - (lastPos - fileData)) - 1);
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ++amount;
 | 
				
			||||||
 | 
					        uint16_t old_delay;
 | 
				
			||||||
 | 
					        memcpy(&old_delay, lastPos + 4, 2);
 | 
				
			||||||
 | 
					        old_delays.push_back(old_delay);
 | 
				
			||||||
 | 
					        lastPos = (char *)memchr(lastPos + 1, '\x21',
 | 
				
			||||||
 | 
					                                 (data.Length() - (lastPos - fileData)) - 1);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      int currentFrame = 0;
 | 
				
			||||||
 | 
					      lastPos = (char *)memchr(fileData, '\x21', data.Length());
 | 
				
			||||||
 | 
					      while (lastPos != NULL) {
 | 
				
			||||||
 | 
					        if (memcmp(lastPos, match, 3) != 0) {
 | 
				
			||||||
 | 
					          lastPos = (char *)memchr(lastPos + 1, '\x21',
 | 
				
			||||||
 | 
					                                   (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 + 4, new_delay, 1);
 | 
				
			||||||
 | 
					        lastPos = (char *)memchr(lastPos + 1, '\x21',
 | 
				
			||||||
 | 
					                                 (data.Length() - (lastPos - fileData)) - 1);
 | 
				
			||||||
 | 
					        ++currentFrame;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      result.Set("data",
 | 
				
			||||||
 | 
					                 Napi::Buffer<char>::Copy(env, fileData, data.Length()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (removeFrames) {
 | 
				
			||||||
        Blob blob;
 | 
					        Blob blob;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        list<Image> frames;
 | 
					        list<Image> frames;
 | 
				
			||||||
        readImages(&frames, Blob(data.Data(), data.Length()));
 | 
					        readImages(&frames, Blob(data.Data(), data.Length()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // if passed a delay, use that. otherwise use the average frame delay.
 | 
					 | 
				
			||||||
    if (delay == 0) {
 | 
					 | 
				
			||||||
      vector<int> old_delays;
 | 
					 | 
				
			||||||
      bool removeFrames = false;
 | 
					 | 
				
			||||||
      for (Image &image : frames) {
 | 
					 | 
				
			||||||
        int animation_delay = image.animationDelay();
 | 
					 | 
				
			||||||
        old_delays.push_back(animation_delay);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      for (Image &image : frames) {
 | 
					 | 
				
			||||||
        int old_delay = image.animationDelay();
 | 
					 | 
				
			||||||
        int new_delay = slow ? old_delay * speed : old_delay / speed;
 | 
					 | 
				
			||||||
        if (!slow && new_delay <= 1) {
 | 
					 | 
				
			||||||
          removeFrames = true;
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        image.animationDelay(new_delay);
 | 
					 | 
				
			||||||
        image.gifDisposeMethod(Magick::BackgroundDispose);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (removeFrames) {
 | 
					 | 
				
			||||||
        for (list<Image>::iterator i = frames.begin(); i != frames.end(); ++i) {
 | 
					        for (list<Image>::iterator i = frames.begin(); i != frames.end(); ++i) {
 | 
				
			||||||
          int index = distance(frames.begin(), i);
 | 
					          int index = distance(frames.begin(), i);
 | 
				
			||||||
          i->animationDelay(old_delays[index]);
 | 
					          i->animationDelay(old_delays[index]);
 | 
				
			||||||
| 
						 | 
					@ -56,27 +95,67 @@ Napi::Value Speed(const Napi::CallbackInfo &info) {
 | 
				
			||||||
            return ++counter % 2 == 0;
 | 
					            return ++counter % 2 == 0;
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for_each(frames.begin(), frames.end(), magickImage(type));
 | 
				
			||||||
 | 
					        writeImages(frames.begin(), frames.end(), &blob);
 | 
				
			||||||
 | 
					        result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
 | 
				
			||||||
 | 
					                                                    blob.length()));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
					      char *lastPos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      bool removeFrames = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      lastPos = (char *)memchr(fileData, '\x21', data.Length());
 | 
				
			||||||
 | 
					      while (lastPos != NULL) {
 | 
				
			||||||
 | 
					        if (memcmp(lastPos, match, 3) != 0) {
 | 
				
			||||||
 | 
					          lastPos = (char *)memchr(lastPos + 1, '\x21',
 | 
				
			||||||
 | 
					                                   (data.Length() - (lastPos - fileData)) - 1);
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        uint16_t old_delay;
 | 
				
			||||||
 | 
					        memcpy(&old_delay, lastPos + 4, 2);
 | 
				
			||||||
        int new_delay = slow ? delay * speed : delay / speed;
 | 
					        int new_delay = slow ? delay * speed : delay / speed;
 | 
				
			||||||
        if (!slow && new_delay <= 1) {
 | 
					        if (!slow && new_delay <= 1) {
 | 
				
			||||||
 | 
					          removeFrames = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (removeFrames) {
 | 
				
			||||||
 | 
					        Blob blob;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        list<Image> frames;
 | 
				
			||||||
 | 
					        readImages(&frames, Blob(data.Data(), data.Length()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (int i = 0; i < speed - 1; ++i) {
 | 
					        for (int i = 0; i < speed - 1; ++i) {
 | 
				
			||||||
          frames.remove_if([counter = 0](const auto x) mutable {
 | 
					          frames.remove_if([counter = 0](const auto x) mutable {
 | 
				
			||||||
            return ++counter % 2 == 0;
 | 
					            return ++counter % 2 == 0;
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        for_each(frames.begin(), frames.end(), animationDelayImage(new_delay));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for_each(frames.begin(), frames.end(), magickImage(type));
 | 
					        for_each(frames.begin(), frames.end(), magickImage(type));
 | 
				
			||||||
 | 
					 | 
				
			||||||
        writeImages(frames.begin(), frames.end(), &blob);
 | 
					        writeImages(frames.begin(), frames.end(), &blob);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    Napi::Object result = Napi::Object::New(env);
 | 
					 | 
				
			||||||
        result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
 | 
					        result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
 | 
				
			||||||
                                                    blob.length()));
 | 
					                                                    blob.length()));
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        while (lastPos != NULL) {
 | 
				
			||||||
 | 
					          if (memcmp(lastPos, match, 3) != 0) {
 | 
				
			||||||
 | 
					            lastPos =
 | 
				
			||||||
 | 
					                (char *)memchr(lastPos + 1, '\x21',
 | 
				
			||||||
 | 
					                               (data.Length() - (lastPos - fileData)) - 1);
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          uint16_t old_delay;
 | 
				
			||||||
 | 
					          memcpy(&old_delay, lastPos + 4, 2);
 | 
				
			||||||
 | 
					          int new_delay = slow ? delay * speed : delay / speed;
 | 
				
			||||||
 | 
					          memset16(lastPos + 4, new_delay, 1);
 | 
				
			||||||
 | 
					          lastPos = (char *)memchr(lastPos + 1, '\x21',
 | 
				
			||||||
 | 
					                                   (data.Length() - (lastPos - fileData)) - 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    result.Set("type", type);
 | 
					    result.Set("type", type);
 | 
				
			||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
  } catch (Napi::Error const &err) {
 | 
					  } catch (Napi::Error const &err) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue