/
SpriteSheetUtils.js
228 lines (209 loc) · 9.53 KB
/
SpriteSheetUtils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
* SpriteSheetUtils
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @module EaselJS
*/
// namespace:
this.createjs = this.createjs||{};
(function() {
"use strict";
// constructor:
/**
* The SpriteSheetUtils class is a collection of static methods for working with {{#crossLink "SpriteSheet"}}{{/crossLink}}s.
* A sprite sheet is a series of images (usually animation frames) combined into a single image on a regular grid. For
* example, an animation consisting of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across
* by 2 high). The SpriteSheetUtils class uses a static interface and should not be instantiated.
* @class SpriteSheetUtils
* @static
**/
function SpriteSheetUtils() {
throw "SpriteSheetUtils cannot be instantiated";
}
// private static properties:
/**
* @property _workingCanvas
* @static
* @type HTMLCanvasElement | Object
* @protected
*/
/**
* @property _workingContext
* @static
* @type CanvasRenderingContext2D
* @protected
*/
var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
if (canvas.getContext) {
SpriteSheetUtils._workingCanvas = canvas;
SpriteSheetUtils._workingContext = canvas.getContext("2d");
canvas.width = canvas.height = 1;
}
// public static methods:
/**
* <b>This is an experimental method, and may be buggy. Please report issues.</b><br/><br/>
* Extends the existing sprite sheet by flipping the original frames horizontally, vertically, or both,
* and adding appropriate animation & frame data. The flipped animations will have a suffix added to their names
* (_h, _v, _hv as appropriate). Make sure the sprite sheet images are fully loaded before using this method.
* <br/><br/>
* For example:<br/>
* SpriteSheetUtils.addFlippedFrames(mySpriteSheet, true, true);
* The above would add frames that are flipped horizontally AND frames that are flipped vertically.
* <br/><br/>
* Note that you can also flip any display object by setting its scaleX or scaleY to a negative value. On some
* browsers (especially those without hardware accelerated canvas) this can result in slightly degraded performance,
* which is why addFlippedFrames is available.
* @method addFlippedFrames
* @static
* @param {SpriteSheet} spriteSheet
* @param {Boolean} horizontal If true, horizontally flipped frames will be added.
* @param {Boolean} vertical If true, vertically flipped frames will be added.
* @param {Boolean} both If true, frames that are flipped both horizontally and vertically will be added.
* @deprecated Modern browsers perform better when flipping via a transform (ex. scaleX=-1) rendering this obsolete.
**/
SpriteSheetUtils.addFlippedFrames = function(spriteSheet, horizontal, vertical, both) {
if (!horizontal && !vertical && !both) { return; }
var count = 0;
if (horizontal) { SpriteSheetUtils._flip(spriteSheet,++count,true,false); }
if (vertical) { SpriteSheetUtils._flip(spriteSheet,++count,false,true); }
if (both) { SpriteSheetUtils._flip(spriteSheet,++count,true,true); }
};
/**
* Returns a single frame of the specified sprite sheet as a new PNG image. An example of when this may be useful is
* to use a spritesheet frame as the source for a bitmap fill.
*
* <strong>WARNING:</strong> In almost all cases it is better to display a single frame using a {{#crossLink "Sprite"}}{{/crossLink}}
* with a {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} call than it is to slice out a frame using this
* method and display it with a Bitmap instance. You can also crop an image using the {{#crossLink "Bitmap/sourceRect"}}{{/crossLink}}
* property of {{#crossLink "Bitmap"}}{{/crossLink}}.
*
* The extractFrame method may cause cross-domain warnings since it accesses pixels directly on the canvas.
* @method extractFrame
* @static
* @param {SpriteSheet} spriteSheet The SpriteSheet instance to extract a frame from.
* @param {Number|String} frameOrAnimation The frame number or animation name to extract. If an animation
* name is specified, only the first frame of the animation will be extracted.
* @return {HTMLImageElement} a single frame of the specified sprite sheet as a new PNG image.
*/
SpriteSheetUtils.extractFrame = function(spriteSheet, frameOrAnimation) {
if (isNaN(frameOrAnimation)) {
frameOrAnimation = spriteSheet.getAnimation(frameOrAnimation).frames[0];
}
var data = spriteSheet.getFrame(frameOrAnimation);
if (!data) { return null; }
var r = data.rect;
var canvas = SpriteSheetUtils._workingCanvas;
canvas.width = r.width;
canvas.height = r.height;
SpriteSheetUtils._workingContext.drawImage(data.image, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height);
var img = document.createElement("img");
img.src = canvas.toDataURL("image/png");
return img;
};
/**
* Merges the rgb channels of one image with the alpha channel of another. This can be used to combine a compressed
* JPEG image containing color data with a PNG32 monochromatic image containing alpha data. With certain types of
* images (those with detail that lend itself to JPEG compression) this can provide significant file size savings
* versus a single RGBA PNG32. This method is very fast (generally on the order of 1-2 ms to run).
* @method mergeAlpha
* @static
* @param {HTMLImageElement} rbgImage The image (or canvas) containing the RGB channels to use.
* @param {HTMLImageElement} alphaImage The image (or canvas) containing the alpha channel to use.
* @param {HTMLCanvasElement} canvas Optional. If specified, this canvas will be used and returned. If not, a new canvas will be created.
* @return {HTMLCanvasElement} A canvas with the combined image data. This can be used as a source for Bitmap or SpriteSheet.
* @deprecated Tools such as ImageAlpha generally provide better results. This will be moved to sandbox in the future.
*/
SpriteSheetUtils.mergeAlpha = function(rgbImage, alphaImage, canvas) {
if (!canvas) { canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); }
canvas.width = Math.max(alphaImage.width, rgbImage.width);
canvas.height = Math.max(alphaImage.height, rgbImage.height);
var ctx = canvas.getContext("2d");
ctx.save();
ctx.drawImage(rgbImage,0,0);
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(alphaImage,0,0);
ctx.restore();
return canvas;
};
// private static methods:
SpriteSheetUtils._flip = function(spriteSheet, count, h, v) {
var imgs = spriteSheet._images;
var canvas = SpriteSheetUtils._workingCanvas;
var ctx = SpriteSheetUtils._workingContext;
var il = imgs.length/count;
for (var i=0;i<il;i++) {
var src = imgs[i];
src.__tmp = i; // a bit hacky, but faster than doing indexOf below.
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width+1,canvas.height+1);
canvas.width = src.width;
canvas.height = src.height;
ctx.setTransform(h?-1:1, 0, 0, v?-1:1, h?src.width:0, v?src.height:0);
ctx.drawImage(src,0,0);
var img = document.createElement("img");
img.src = canvas.toDataURL("image/png");
// work around a strange bug in Safari:
img.width = src.width;
img.height = src.height;
imgs.push(img);
}
var frames = spriteSheet._frames;
var fl = frames.length/count;
for (i=0;i<fl;i++) {
src = frames[i];
var rect = src.rect.clone();
img = imgs[src.image.__tmp+il*count];
var frame = {image:img,rect:rect,regX:src.regX,regY:src.regY};
if (h) {
rect.x = img.width-rect.x-rect.width; // update rect
frame.regX = rect.width-src.regX; // update registration point
}
if (v) {
rect.y = img.height-rect.y-rect.height; // update rect
frame.regY = rect.height-src.regY; // update registration point
}
frames.push(frame);
}
var sfx = "_"+(h?"h":"")+(v?"v":"");
var names = spriteSheet._animations;
var data = spriteSheet._data;
var al = names.length/count;
for (i=0;i<al;i++) {
var name = names[i];
src = data[name];
var anim = {name:name+sfx,speed:src.speed,next:src.next,frames:[]};
if (src.next) { anim.next += sfx; }
frames = src.frames;
for (var j=0,l=frames.length;j<l;j++) {
anim.frames.push(frames[j]+fl*count);
}
data[anim.name] = anim;
names.push(anim.name);
}
};
createjs.SpriteSheetUtils = SpriteSheetUtils;
}());