Iavra

Iavra Gif

Oct 30th, 2015 (edited)
6,017
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*:
  2.  * @plugindesc Allows the usage of animated images (like Gifs), which are present in form of an image strip.
  3.  * <Iavra Gif>
  4.  * @author Iavra
  5.  *
  6.  * @param File Name Format
  7.  * @desc Used as an identifier for animated images. We capture the number of frames and the update rate of the animation.
  8.  * @default _animated_(\d+)_(\d+)
  9.  *
  10.  * @help
  11.  * Allows the usage of animated images in RMMV. They have to be converted to image strips beforehand, which is an image
  12.  * with the dimensions [(frame width * number of frames) x (frame height)] (width x height). So, if you have an image with
  13.  * dimensions of [50x60] and 4 animation frames, the resulting strip will be [200x60] and consist of all 4 frames in a
  14.  * row.
  15.  *
  16.  * The image has to be named in a certain way, so the plugin knows which images it needs to animate. When using the default
  17.  * value for the parameter "File Name Format", the strip above could be named "myImage_animated_4_100.png". The last number
  18.  * tells the plugin, how fast the animation should be played. In this case, we change the displayed image every 100 frames.
  19.  *
  20.  * The plugin has been tested for simple pictures, characters and battle background and works even when tinting them.
  21.  */
  22.  
  23. var Imported = Imported || {};
  24. Imported.iavra_gif = true;
  25.  
  26. //=============================================================================
  27. // namespace IAVRA
  28. //=============================================================================
  29.  
  30. var IAVRA = IAVRA || {};
  31.  
  32. (function() {
  33.     "use strict";
  34.    
  35.     /**
  36.      * Since PluginManager.parameters() breaks when the plugin file is renamed, we are using our own solution.
  37.      */
  38.     var _params = (function($) {
  39.         return {
  40.             fileFormat: new RegExp($['File Name Format'])
  41.         };
  42.     })($plugins.filter(function(p) { return p.description.contains('<Iavra Gif>'); })[0].parameters);
  43.    
  44.     /**
  45.     * This property actually overrides the "_bitmap" attribute on the sprite class, which itself is used by the "bitmap"
  46.     * property. This way we actually intercept all access to an internal variable of a class. We use the same override
  47.     * for Sprite and TilingSprite, so it has been outsourced to its own function.
  48.     */
  49.     var defineBitmapProperty = function(spriteClass) {
  50.         Object.defineProperty(spriteClass.prototype, '_bitmap', {
  51.             /**
  52.              * When the bitmap object of a sprite is accessed, we determine if it's animated and return the actual
  53.              * Bitmap object of the current animation frame.
  54.              */
  55.             get: function() {
  56.                 if(this._iavra_gif.gif && this._iavra_gif.gif.constructor === IAVRA.GIF.Gif) {
  57.                     return this._iavra_gif.gif._frames[this._iavra_gif.currentFrame];
  58.                 } else {
  59.                     return this._iavra_gif.gif;
  60.                 }
  61.             },
  62.             /**
  63.              * When setting the bitmap object on a sprite, we also reset its current frame and animation timer, so we
  64.              * can start the animation from the beginning.
  65.              */
  66.             set: function(value) {
  67.                 this._iavra_gif = { gif: value, currentFrame: 0, timer: 0 };
  68.             }
  69.         });
  70.     };
  71.    
  72.     //=============================================================================
  73.     // namespace IAVRA.GIF
  74.     //=============================================================================
  75.    
  76.     IAVRA.GIF = {};
  77.    
  78.     //=============================================================================
  79.     // class IAVRA.GIF.Gif
  80.     //=============================================================================
  81.    
  82.     IAVRA.GIF.Gif = function() {};
  83.     (function($) {
  84.         $.prototype.constructor = $;
  85.        
  86.         /**
  87.          * When loading an image, we determine how many frames it has and create that many Bitmap objects. The rest works
  88.          * similar to a normal Bitmap, since we first have to wait for the image to load before we can continue.
  89.          */
  90.         $.load = function(url, params) {
  91.             var gif = new IAVRA.GIF.Gif();
  92.             gif._numFrames = parseInt(params[1]) || 1;
  93.             gif._frameRate = parseInt(params[2]) || 0;
  94.             gif._frames = [];
  95.             for(var index = 0; index < gif._numFrames; ++index) {
  96.                 var bitmap = new Bitmap();
  97.                 bitmap._isLoading = true;
  98.                 gif._frames.push(bitmap);
  99.             }
  100.             gif._image = new Image();
  101.             gif._image.src = url;
  102.             gif._image.onload = IAVRA.GIF.Gif.prototype._onLoad.bind(gif);
  103.             gif._image.onerror = IAVRA.GIF.Gif.prototype._onError.bind(gif);
  104.             gif._image._url = url;
  105.             return gif;
  106.         };
  107.        
  108.         /**
  109.          * When the image is loaded, we iterate over the Bitmap objects previously created and create a new canvas for
  110.          * each one. This is used to split the loaded image in multiple smaller ones. This way, sprites only ever see with
  111.          * a single animation frame and this can actually work with TilingSprite (i hate you).
  112.          */
  113.         $.prototype._onLoad = function() {
  114.             this._frames.forEach(function(bitmap, index) {
  115.                 // Calculate the width of a single frame and create a new canvas object for every Bitmap. This is used to
  116.                 // draw the actual animation frame of the Bitmap.
  117.                 var frameWidth = this._image.width / this._numFrames;
  118.                 var canvas = document.createElement('canvas');
  119.                 var context = canvas.getContext('2d');
  120.                 canvas.width = frameWidth;
  121.                 canvas.height = Math.max(this._image.height || 0, 1);
  122.                 context.drawImage(this._image, -index * frameWidth, 0);
  123.                 // This is basically the same as Bitmap.prototype._onLoad(), only that we aren't using the loaded image
  124.                 // directly to draw the bitmap, but our canvas object.
  125.                 bitmap._isLoading = false;
  126.                 bitmap.resize(frameWidth, this._image.height);
  127.                 bitmap._context.drawImage(canvas, 0, 0);
  128.                 bitmap._setDirty();
  129.                 bitmap._callLoadListeners();
  130.             }, this);
  131.         };
  132.        
  133.         /**
  134.          * If there was an error loading the image, we execute the _onError() callback on every contained Bitmap.
  135.          */
  136.         $.prototype._onError = function() {
  137.             this._frames.forEach(function(bitmap) { bitmap._onError(); });
  138.         };
  139.        
  140.         /**
  141.          * These functions are needed for the ImageManager, since it's the only class in the entire engine that actually
  142.          * interacts with the container, instead of one of it's Bitmap children. Overring the last 2 functions with empty
  143.          * ones actually seems to do nothing. I tried to propagate listeners to all frames, but it didn't change anything,
  144.          * so i'll just leave it at this.
  145.          */
  146.         $.prototype.isReady = function() { return this._frames.every(function(bitmap) { return bitmap.isReady(); }); }
  147.         $.prototype.isError = function() { return this._frames.some(function(bitmap) { return bitmap.isError(); }); }
  148.         $.prototype.addLoadListener = function(listner) {};
  149.         $.prototype.rotateHue = function(hue) {};
  150.        
  151.         /**
  152.          * Calculate the current frame of the animation, given a specific timer. This way, each Sprite can calculate it's
  153.          * own animation and run independent from each other.
  154.          */
  155.         $.prototype._currentAnimationFrame = function(timer) {
  156.             return Math.floor((timer % (this._numFrames * this._frameRate)) / this._frameRate);
  157.         };
  158.        
  159.     })(IAVRA.GIF.Gif);
  160.    
  161.     //=============================================================================
  162.     // class Sprite
  163.     //=============================================================================
  164.    
  165.     (function($) {
  166.        defineBitmapProperty($);
  167.        
  168.         /**
  169.          * When updating, we fetch the current animation frame from the container and refresh, if it's different from our
  170.          * current frame.
  171.          */
  172.         var _alias_update = $.prototype.update;
  173.         $.prototype.update = function() {
  174.             if(this._iavra_gif.gif && this._iavra_gif.gif.constructor === IAVRA.GIF.Gif) {
  175.                 var nextFrame = this._iavra_gif.gif._currentAnimationFrame(++this._iavra_gif.timer);
  176.                 if(nextFrame !== this._iavra_gif.currentFrame) {
  177.                     this._iavra_gif.currentFrame = nextFrame;
  178.                     this._refresh();
  179.                 }
  180.             }
  181.             _alias_update.call(this);
  182.         };
  183.        
  184.     })(Sprite);
  185.    
  186.     //=============================================================================
  187.     // class TilingSprite
  188.     //=============================================================================
  189.    
  190.     (function($) {
  191.         defineBitmapProperty($);
  192.        
  193.         /**
  194.          * When updating, we fetch the current animation frame from the container and refresh, if it's different from our
  195.          * current frame. Since TilingSprite doesn't actually work with the Bitmap object itself, but with its baseTexture,
  196.          * we have to call _onBitmapLoad() instead of _refresh(). This will cause the Sprite to reload the texture (this
  197.          * time from the next frame in the animation).
  198.          */
  199.         var _alias_update = $.prototype.update;
  200.         $.prototype.update = function() {
  201.             if(this._iavra_gif.gif && this._iavra_gif.gif.constructor === IAVRA.GIF.Gif) {
  202.                 var nextFrame = this._iavra_gif.gif._currentAnimationFrame(++this._iavra_gif.timer);
  203.                 if(nextFrame !== this._iavra_gif.currentFrame) {
  204.                     this._iavra_gif.currentFrame = nextFrame;
  205.                     this._onBitmapLoad();
  206.                 }
  207.             }
  208.             _alias_update.call(this);
  209.         };
  210.        
  211.     })(TilingSprite);
  212.    
  213.     //=============================================================================
  214.     // class Bitmap
  215.     //=============================================================================
  216.    
  217.     (function($) {
  218.        
  219.         /**
  220.          * When loading a new Bitmap, we determine if it's animated (by matching the file name) and load our container
  221.          * class, instead.
  222.          */
  223.         var _alias_load = $.load;
  224.         $.load = function(url) {
  225.             var match = _params.fileFormat.exec(url);
  226.             return match ? IAVRA.GIF.Gif.load(url, match) : _alias_load.call(this, url);
  227.         };
  228.        
  229.     })(Bitmap);
  230.  
  231. })();
Add Comment
Please, Sign In to add comment