melonjs 10.7.0 → 10.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/melonjs.js +1488 -666
  2. package/dist/melonjs.min.js +4 -4
  3. package/dist/melonjs.module.d.ts +929 -202
  4. package/dist/melonjs.module.js +1575 -777
  5. package/package.json +9 -9
  6. package/src/camera/camera2d.js +1 -1
  7. package/src/entity/entity.js +6 -7
  8. package/src/geometries/ellipse.js +10 -11
  9. package/src/geometries/line.js +3 -3
  10. package/src/geometries/path2d.js +319 -0
  11. package/src/geometries/poly.js +11 -11
  12. package/src/geometries/rectangle.js +15 -15
  13. package/src/geometries/roundrect.js +164 -0
  14. package/src/index.js +5 -1
  15. package/src/input/gamepad.js +2 -2
  16. package/src/input/pointerevent.js +1 -1
  17. package/src/lang/deprecated.js +1 -1
  18. package/src/level/tiled/TMXLayer.js +1 -1
  19. package/src/level/tiled/TMXObject.js +9 -12
  20. package/src/level/tiled/TMXTileMap.js +23 -4
  21. package/src/level/tiled/renderer/TMXHexagonalRenderer.js +1 -1
  22. package/src/level/tiled/renderer/TMXIsometricRenderer.js +1 -1
  23. package/src/level/tiled/renderer/TMXOrthogonalRenderer.js +1 -1
  24. package/src/level/tiled/renderer/TMXRenderer.js +1 -1
  25. package/src/level/tiled/renderer/TMXStaggeredRenderer.js +1 -1
  26. package/src/loader/loader.js +4 -4
  27. package/src/loader/loadingscreen.js +1 -1
  28. package/src/math/color.js +1 -1
  29. package/src/math/matrix2.js +1 -1
  30. package/src/math/matrix3.js +1 -1
  31. package/src/math/observable_vector2.js +1 -1
  32. package/src/math/observable_vector3.js +1 -1
  33. package/src/math/vector2.js +1 -1
  34. package/src/math/vector3.js +1 -1
  35. package/src/particles/emitter.js +23 -14
  36. package/src/particles/particle.js +3 -2
  37. package/src/physics/body.js +67 -51
  38. package/src/physics/bounds.js +8 -9
  39. package/src/physics/world.js +1 -1
  40. package/src/polyfill/index.js +1 -0
  41. package/src/polyfill/roundrect.js +235 -0
  42. package/src/renderable/collectable.js +9 -2
  43. package/src/renderable/colorlayer.js +1 -1
  44. package/src/renderable/container.js +1 -1
  45. package/src/renderable/imagelayer.js +1 -1
  46. package/src/renderable/renderable.js +2 -2
  47. package/src/renderable/sprite.js +2 -3
  48. package/src/renderable/trigger.js +10 -4
  49. package/src/state/stage.js +1 -1
  50. package/src/state/state.js +1 -1
  51. package/src/system/device.js +10 -8
  52. package/src/system/pooling.js +156 -149
  53. package/src/text/bitmaptext.js +1 -1
  54. package/src/text/text.js +1 -1
  55. package/src/utils/utils.js +2 -2
  56. package/src/video/canvas/canvas_renderer.js +83 -39
  57. package/src/video/renderer.js +36 -16
  58. package/src/video/texture.js +1 -1
  59. package/src/video/webgl/glshader.js +29 -193
  60. package/src/video/webgl/utils/attributes.js +16 -0
  61. package/src/video/webgl/utils/precision.js +11 -0
  62. package/src/video/webgl/utils/program.js +58 -0
  63. package/src/video/webgl/utils/string.js +16 -0
  64. package/src/video/webgl/utils/uniforms.js +87 -0
  65. package/src/video/webgl/webgl_compositor.js +1 -14
  66. package/src/video/webgl/webgl_renderer.js +124 -182
@@ -1,7 +1,5 @@
1
- var objectClass = {};
2
- var instance_counter = 0;
3
-
4
1
  /**
2
+ * @classdesc
5
3
  * This object is used for object pooling - a technique that might speed up your game if used properly.<br>
6
4
  * If some of your classes will be instantiated and removed a lot at a time, it is a
7
5
  * good idea to add the class to this object pool. A separate pool for that class
@@ -12,172 +10,181 @@ var instance_counter = 0;
12
10
  * which means, that on level loading the engine will try to instantiate every object
13
11
  * found in the map, based on the user defined name in each Object Properties<br>
14
12
  * <img src="images/object_properties.png"/><br>
15
- * @namespace pool
13
+ * @see {@link pool} a default global instance of ObjectPool
16
14
  */
15
+ class ObjectPool {
17
16
 
18
- /**
19
- * register an object to the pool. <br>
20
- * Pooling must be set to true if more than one such objects will be created. <br>
21
- * (Note: for an object to be poolable, it must implements a `onResetEvent` method)
22
- * @function pool.register
23
- * @param {string} className as defined in the Name field of the Object Properties (in Tiled)
24
- * @param {object} classObj corresponding Class to be instantiated
25
- * @param {boolean} [recycling=false] enables object recycling for the specified class
26
- * @example
27
- * // implement CherryEntity
28
- * class CherryEntity extends Spritesheet {
29
- * onResetEvent() {
30
- * // reset object mutable properties
31
- * this.lifeBar = 100;
32
- * }
33
- * };
34
- * // add our users defined entities in the object pool and enable object recycling
35
- * me.pool.register("cherryentity", CherryEntity, true);
36
- */
37
- export function register(className, classObj, recycling = false) {
38
- if (typeof (classObj) !== "undefined") {
39
- objectClass[className] = {
40
- "class" : classObj,
41
- "pool" : (recycling ? [] : undefined)
42
- };
43
- } else {
44
- throw new Error("Cannot register object '" + className + "', invalid class");
17
+ constructor() {
18
+ this.objectClass = {};
19
+ this.instance_counter = 0;
20
+ }
21
+
22
+ /**
23
+ * register an object to the pool. <br>
24
+ * Pooling must be set to true if more than one such objects will be created. <br>
25
+ * (Note: for an object to be poolable, it must implements a `onResetEvent` method)
26
+ * @param {string} className as defined in the Name field of the Object Properties (in Tiled)
27
+ * @param {object} classObj corresponding Class to be instantiated
28
+ * @param {boolean} [recycling=false] enables object recycling for the specified class
29
+ * @example
30
+ * // implement CherryEntity
31
+ * class CherryEntity extends Spritesheet {
32
+ * onResetEvent() {
33
+ * // reset object mutable properties
34
+ * this.lifeBar = 100;
35
+ * }
36
+ * };
37
+ * // add our users defined entities in the object pool and enable object recycling
38
+ * me.pool.register("cherryentity", CherryEntity, true);
39
+ */
40
+ register(className, classObj, recycling = false) {
41
+ if (typeof (classObj) !== "undefined") {
42
+ this.objectClass[className] = {
43
+ "class" : classObj,
44
+ "pool" : (recycling ? [] : undefined)
45
+ };
46
+ } else {
47
+ throw new Error("Cannot register object '" + className + "', invalid class");
48
+ }
45
49
  }
46
- };
47
50
 
48
- /**
49
- * Pull a new instance of the requested object (if added into the object pool)
50
- * @function pool.pull
51
- * @param {string} name as used in {@link pool.register}
52
- * @param {object} [...arguments] arguments to be passed when instantiating/reinitializing the object
53
- * @returns {object} the instance of the requested object
54
- * @example
55
- * me.pool.register("bullet", BulletEntity, true);
56
- * me.pool.register("enemy", EnemyEntity, true);
57
- * // ...
58
- * // when we need to manually create a new bullet:
59
- * var bullet = me.pool.pull("bullet", x, y, direction);
60
- * // ...
61
- * // params aren't a fixed number
62
- * // when we need new enemy we can add more params, that the object construct requires:
63
- * var enemy = me.pool.pull("enemy", x, y, direction, speed, power, life);
64
- * // ...
65
- * // when we want to destroy existing object, the remove
66
- * // function will ensure the object can then be reallocated later
67
- * me.game.world.removeChild(enemy);
68
- * me.game.world.removeChild(bullet);
69
- */
70
- export function pull(name) {
71
- var args = new Array(arguments.length);
72
- for (var i = 0; i < arguments.length; i++) {
73
- args[i] = arguments[i];
74
- }
75
- var className = objectClass[name];
76
- if (className) {
77
- var proto = className["class"],
78
- poolArray = className.pool,
79
- obj;
51
+ /**
52
+ * Pull a new instance of the requested object (if added into the object pool)
53
+ * @param {string} name as used in {@link pool.register}
54
+ * @param {object} [...arguments] arguments to be passed when instantiating/reinitializing the object
55
+ * @returns {object} the instance of the requested object
56
+ * @example
57
+ * me.pool.register("bullet", BulletEntity, true);
58
+ * me.pool.register("enemy", EnemyEntity, true);
59
+ * // ...
60
+ * // when we need to manually create a new bullet:
61
+ * var bullet = me.pool.pull("bullet", x, y, direction);
62
+ * // ...
63
+ * // params aren't a fixed number
64
+ * // when we need new enemy we can add more params, that the object construct requires:
65
+ * var enemy = me.pool.pull("enemy", x, y, direction, speed, power, life);
66
+ * // ...
67
+ * // when we want to destroy existing object, the remove
68
+ * // function will ensure the object can then be reallocated later
69
+ * me.game.world.removeChild(enemy);
70
+ * me.game.world.removeChild(bullet);
71
+ */
72
+ pull(name) {
73
+ var args = new Array(arguments.length);
74
+ for (var i = 0; i < arguments.length; i++) {
75
+ args[i] = arguments[i];
76
+ }
77
+ var className = this.objectClass[name];
78
+ if (className) {
79
+ var proto = className["class"],
80
+ poolArray = className.pool,
81
+ obj;
80
82
 
81
- if (poolArray && ((obj = poolArray.pop()))) {
82
- // pull an existing instance from the pool
83
- args.shift();
84
- // call the object onResetEvent function if defined
85
- if (typeof(obj.onResetEvent) === "function") {
86
- obj.onResetEvent.apply(obj, args);
83
+ if (poolArray && ((obj = poolArray.pop()))) {
84
+ // pull an existing instance from the pool
85
+ args.shift();
86
+ // call the object onResetEvent function if defined
87
+ if (typeof(obj.onResetEvent) === "function") {
88
+ obj.onResetEvent.apply(obj, args);
89
+ }
90
+ this.instance_counter--;
87
91
  }
88
- instance_counter--;
89
- }
90
- else {
91
- // create a new instance
92
- args[0] = proto;
93
- obj = new (proto.bind.apply(proto, args))();
94
- if (poolArray) {
95
- obj.className = name;
92
+ else {
93
+ // create a new instance
94
+ args[0] = proto;
95
+ obj = new (proto.bind.apply(proto, args))();
96
+ if (poolArray) {
97
+ obj.className = name;
98
+ }
96
99
  }
100
+ return obj;
97
101
  }
98
- return obj;
102
+ throw new Error("Cannot instantiate object of type '" + name + "'");
99
103
  }
100
- throw new Error("Cannot instantiate object of type '" + name + "'");
101
- };
102
104
 
103
- /**
104
- * purge the object pool from any inactive object <br>
105
- * Object pooling must be enabled for this function to work<br>
106
- * note: this will trigger the garbage collector
107
- * @function pool.purge
108
- */
109
- export function purge() {
110
- for (var className in objectClass) {
111
- if (objectClass[className]) {
112
- objectClass[className].pool = [];
105
+ /**
106
+ * purge the object pool from any inactive object <br>
107
+ * Object pooling must be enabled for this function to work<br>
108
+ * note: this will trigger the garbage collector
109
+ */
110
+ purge() {
111
+ for (var className in this.objectClass) {
112
+ if (this.objectClass[className]) {
113
+ this.objectClass[className].pool = [];
114
+ }
113
115
  }
116
+ this.instance_counter = 0;
114
117
  }
115
- instance_counter = 0;
116
- };
117
118
 
118
- /**
119
- * Push back an object instance into the object pool <br>
120
- * Object pooling for the object class must be enabled,
121
- * and object must have been instantiated using {@link pool#pull},
122
- * otherwise this function won't work
123
- * @function pool.push
124
- * @throws will throw an error if the object cannot be recycled
125
- * @param {object} obj instance to be recycled
126
- * @param {boolean} [throwOnError=true] throw an exception if the object cannot be recycled
127
- * @returns {boolean} true if the object was successfully recycled in the object pool
128
- */
129
- export function push(obj, throwOnError = true) {
130
- if (!poolable(obj)) {
131
- if (throwOnError === true ) {
132
- throw new Error("me.pool: object " + obj + " cannot be recycled");
133
- } else {
134
- return false;
119
+ /**
120
+ * Push back an object instance into the object pool <br>
121
+ * Object pooling for the object class must be enabled,
122
+ * and object must have been instantiated using {@link pool#pull},
123
+ * otherwise this function won't work
124
+ * @throws will throw an error if the object cannot be recycled
125
+ * @param {object} obj instance to be recycled
126
+ * @param {boolean} [throwOnError=true] throw an exception if the object cannot be recycled
127
+ * @returns {boolean} true if the object was successfully recycled in the object pool
128
+ */
129
+ push(obj, throwOnError = true) {
130
+ if (!this.poolable(obj)) {
131
+ if (throwOnError === true ) {
132
+ throw new Error("me.pool: object " + obj + " cannot be recycled");
133
+ } else {
134
+ return false;
135
+ }
135
136
  }
136
- }
137
137
 
138
- // store back the object instance for later recycling
139
- objectClass[obj.className].pool.push(obj);
140
- instance_counter++;
138
+ // store back the object instance for later recycling
139
+ this.objectClass[obj.className].pool.push(obj);
140
+ this.instance_counter++;
141
141
 
142
- return true;
143
- };
142
+ return true;
143
+ }
144
144
 
145
- /**
146
- * Check if an object with the provided name is registered
147
- * @function pool.exists
148
- * @param {string} name of the registered object class
149
- * @returns {boolean} true if the classname is registered
150
- */
151
- export function exists(name) {
152
- return name in objectClass;
153
- };
145
+ /**
146
+ * Check if an object with the provided name is registered
147
+ * @param {string} name of the registered object class
148
+ * @returns {boolean} true if the classname is registered
149
+ */
150
+ exists(name) {
151
+ return name in this.objectClass;
152
+ };
154
153
 
155
- /**
156
- * Check if an object is poolable
157
- * (was properly registered with the recycling feature enable)
158
- * @function pool.poolable
159
- * @see pool.register
160
- * @param {object} obj object to be checked
161
- * @returns {boolean} true if the object is poolable
162
- * @example
163
- * if (!me.pool.poolable(myCherryEntity)) {
164
- * // object was not properly registered
165
- * }
166
- */
167
- export function poolable(obj) {
168
- var className = obj.className;
169
- return (typeof className !== "undefined") &&
170
- (typeof obj.onResetEvent === "function") &&
171
- (className in objectClass) &&
172
- (objectClass[className].pool !== "undefined");
154
+ /**
155
+ * Check if an object is poolable
156
+ * (was properly registered with the recycling feature enable)
157
+ * @see register
158
+ * @param {object} obj object to be checked
159
+ * @returns {boolean} true if the object is poolable
160
+ * @example
161
+ * if (!me.pool.poolable(myCherryEntity)) {
162
+ * // object was not properly registered
163
+ * }
164
+ */
165
+ poolable(obj) {
166
+ var className = obj.className;
167
+ return (typeof className !== "undefined") &&
168
+ (typeof obj.onResetEvent === "function") &&
169
+ (className in this.objectClass) &&
170
+ (this.objectClass[className].pool !== "undefined");
171
+
172
+ }
173
173
 
174
+ /**
175
+ * returns the amount of object instance currently in the pool
176
+ * @returns {number} amount of object instance
177
+ */
178
+ getInstanceCount() {
179
+ return this.instance_counter;
180
+ }
174
181
  };
175
182
 
176
183
  /**
177
- * returns the amount of object instance currently in the pool
178
- * @function pool.getInstanceCount
179
- * @returns {number} amount of object instance
184
+ * a default global object pool instance
185
+ * @public
186
+ * @type {ObjectPool}
180
187
  */
181
- export function getInstanceCount() {
182
- return instance_counter;
183
- };
188
+ var pool = new ObjectPool();
189
+
190
+ export default pool;
@@ -1,6 +1,6 @@
1
1
  import Color from "./../math/color.js";
2
2
  import * as stringUtil from "./../utils/string.js";
3
- import * as pool from "./../system/pooling.js";
3
+ import pool from "./../system/pooling.js";
4
4
  import loader from "./../loader/loader.js";
5
5
  import Renderable from "./../renderable/renderable.js";
6
6
  import TextMetrics from "./textmetrics.js";
package/src/text/text.js CHANGED
@@ -2,7 +2,7 @@ import Color from "./../math/color.js";
2
2
  import WebGLRenderer from "./../video/webgl/webgl_renderer.js";
3
3
  import { renderer as globalRenderer, createCanvas } from "./../video/video.js";
4
4
  import { trimRight } from "./../utils/string.js";
5
- import * as pool from "./../system/pooling.js";
5
+ import pool from "./../system/pooling.js";
6
6
  import Renderable from "./../renderable/renderable.js";
7
7
  import { nextPowerOfTwo } from "./../math/math.js";
8
8
  import setContextStyle from "./textstyle.js";
@@ -120,14 +120,14 @@ var utils = {
120
120
  // never cache if a url is passed as parameter
121
121
  var index = url.indexOf("#");
122
122
  if (index !== -1) {
123
- url = url.substr(index, url.length);
123
+ url = url.slice(index, url.length);
124
124
  } else {
125
125
  return hash;
126
126
  }
127
127
  }
128
128
 
129
129
  // parse the url
130
- url.substr(1).split("&").filter(function (value) {
130
+ url.slice(1).split("&").filter(function (value) {
131
131
  return (value !== "");
132
132
  }).forEach(function (value) {
133
133
  var kv = value.split("=");
@@ -2,6 +2,7 @@ import Color from "./../../math/color.js";
2
2
  import Renderer from "./../renderer.js";
3
3
  import TextureCache from "./../texture_cache.js";
4
4
  import Ellipse from "./../../geometries/ellipse.js";
5
+ import RoundRect from "./../../geometries/roundrect.js";
5
6
  import { createCanvas } from "./../video.js";
6
7
 
7
8
 
@@ -65,7 +66,6 @@ class CanvasRenderer extends Renderer {
65
66
  this.getScreenCanvas().addEventListener("contextrestored", () => {
66
67
  this.isContextValid = true;
67
68
  event.emit(event.ONCONTEXT_RESTORED, this);
68
- me.game.repaint();
69
69
  }, false );
70
70
  }
71
71
 
@@ -190,7 +190,7 @@ class CanvasRenderer extends Renderer {
190
190
  * @param {number} height The rectangle's height.
191
191
  */
192
192
  clearRect(x, y, width, height) {
193
- this.backBufferContext2D.clearRect(x, y, width, height);
193
+ this.getContext().clearRect(x, y, width, height);
194
194
  }
195
195
 
196
196
  /**
@@ -209,7 +209,7 @@ class CanvasRenderer extends Renderer {
209
209
  * var basic = renderer.createPattern(image, "no-repeat");
210
210
  */
211
211
  createPattern(image, repeat) {
212
- return this.backBufferContext2D.createPattern(image, repeat);
212
+ return this.getContext().createPattern(image, repeat);
213
213
  }
214
214
 
215
215
  /**
@@ -235,10 +235,11 @@ class CanvasRenderer extends Renderer {
235
235
  * renderer.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
236
236
  */
237
237
  drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
238
- if (this.backBufferContext2D.globalAlpha < 1 / 255) {
238
+ if (this.getGlobalAlpha() < 1 / 255) {
239
239
  // Fast path: don't draw fully transparent
240
240
  return;
241
241
  }
242
+ var context = this.getContext();
242
243
 
243
244
  if (typeof sw === "undefined") {
244
245
  sw = dw = image.width;
@@ -272,7 +273,7 @@ class CanvasRenderer extends Renderer {
272
273
  // get a tinted version of this image from the texture cache
273
274
  source = this.cache.tint(image, this.currentTint.toRGB());
274
275
  }
275
- this.backBufferContext2D.drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh);
276
+ context.drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh);
276
277
  }
277
278
 
278
279
  /**
@@ -288,14 +289,15 @@ class CanvasRenderer extends Renderer {
288
289
  * @see CanvasRenderer#createPattern
289
290
  */
290
291
  drawPattern(pattern, x, y, width, height) {
291
- if (this.backBufferContext2D.globalAlpha < 1 / 255) {
292
+ if (this.getGlobalAlpha() < 1 / 255) {
292
293
  // Fast path: don't draw fully transparent
293
294
  return;
294
295
  }
295
- var fillStyle = this.backBufferContext2D.fillStyle;
296
- this.backBufferContext2D.fillStyle = pattern;
297
- this.backBufferContext2D.fillRect(x, y, width, height);
298
- this.backBufferContext2D.fillStyle = fillStyle;
296
+ var context = this.getContext();
297
+ var fillStyle = context.fillStyle;
298
+ context.fillStyle = pattern;
299
+ context.fillRect(x, y, width, height);
300
+ context.fillStyle = fillStyle;
299
301
  }
300
302
 
301
303
  /**
@@ -312,12 +314,12 @@ class CanvasRenderer extends Renderer {
312
314
  * @param {boolean} [fill=false] also fill the shape with the current color if true
313
315
  */
314
316
  strokeArc(x, y, radius, start, end, antiClockwise, fill = false) {
315
- var context = this.backBufferContext2D;
316
-
317
- if (context.globalAlpha < 1 / 255) {
317
+ if (this.getGlobalAlpha() < 1 / 255) {
318
318
  // Fast path: don't draw fully transparent
319
319
  return;
320
320
  }
321
+ var context = this.getContext();
322
+
321
323
  context.translate(x, y);
322
324
  context.beginPath();
323
325
  context.arc(0, 0, radius, start, end, antiClockwise || false);
@@ -353,12 +355,11 @@ class CanvasRenderer extends Renderer {
353
355
  * @param {boolean} [fill=false] also fill the shape with the current color if true
354
356
  */
355
357
  strokeEllipse(x, y, w, h, fill = false) {
356
- var context = this.backBufferContext2D;
357
-
358
- if (context.globalAlpha < 1 / 255) {
358
+ if (this.getGlobalAlpha() < 1 / 255) {
359
359
  // Fast path: don't draw fully transparent
360
360
  return;
361
361
  }
362
+ var context = this.getContext();
362
363
 
363
364
  var hw = w,
364
365
  hh = h,
@@ -409,9 +410,7 @@ class CanvasRenderer extends Renderer {
409
410
  * @param {number} endY the end y coordinate
410
411
  */
411
412
  strokeLine(startX, startY, endX, endY) {
412
- var context = this.backBufferContext2D;
413
-
414
- if (context < 1 / 255) {
413
+ if (this.getGlobalAlpha() < 1 / 255) {
415
414
  // Fast path: don't draw fully transparent
416
415
  return;
417
416
  }
@@ -445,12 +444,11 @@ class CanvasRenderer extends Renderer {
445
444
  * @param {boolean} [fill=false] also fill the shape with the current color if true
446
445
  */
447
446
  strokePolygon(poly, fill = false) {
448
- var context = this.backBufferContext2D;
449
-
450
- if (context.globalAlpha < 1 / 255) {
447
+ if (this.getGlobalAlpha() < 1 / 255) {
451
448
  // Fast path: don't draw fully transparent
452
449
  return;
453
450
  }
451
+ var context = this.getContext();
454
452
 
455
453
  this.translate(poly.pos.x, poly.pos.y);
456
454
  context.beginPath();
@@ -489,15 +487,13 @@ class CanvasRenderer extends Renderer {
489
487
  * @param {boolean} [fill=false] also fill the shape with the current color if true
490
488
  */
491
489
  strokeRect(x, y, width, height, fill = false) {
492
- if (fill === true ) {
493
- this.fillRect(x, y, width, height);
494
- } else {
495
- if (this.backBufferContext2D.globalAlpha < 1 / 255) {
496
- // Fast path: don't draw fully transparent
497
- return;
498
- }
499
- this.backBufferContext2D.strokeRect(x, y, width, height);
490
+ if (this.getGlobalAlpha() < 1 / 255) {
491
+ // Fast path: don't draw fully transparent
492
+ return;
500
493
  }
494
+ var context = this.getContext();
495
+
496
+ context[fill === true ? "fillRect" : "strokeRect"](x, y, width, height);
501
497
  }
502
498
 
503
499
  /**
@@ -511,11 +507,46 @@ class CanvasRenderer extends Renderer {
511
507
  * @param {number} height
512
508
  */
513
509
  fillRect(x, y, width, height) {
514
- if (this.backBufferContext2D.globalAlpha < 1 / 255) {
510
+ this.strokeRect(x, y, width, height, true);
511
+ }
512
+
513
+ /**
514
+ * Stroke a rounded rectangle at the specified coordinates
515
+ * @name strokeRoundRect
516
+ * @memberof CanvasRenderer.prototype
517
+ * @function
518
+ * @param {number} x
519
+ * @param {number} y
520
+ * @param {number} width
521
+ * @param {number} height
522
+ * @param {number} radius
523
+ * @param {boolean} [fill=false] also fill the shape with the current color if true
524
+ */
525
+ strokeRoundRect(x, y, width, height, radius, fill = false) {
526
+ if (this.getGlobalAlpha() < 1 / 255) {
515
527
  // Fast path: don't draw fully transparent
516
528
  return;
517
529
  }
518
- this.backBufferContext2D.fillRect(x, y, width, height);
530
+ var context = this.getContext();
531
+
532
+ context.beginPath();
533
+ context.roundRect(x, y, width, height, radius);
534
+ context[fill === true ? "fill" : "stroke"]();
535
+ }
536
+
537
+ /**
538
+ * Draw a rounded filled rectangle at the specified coordinates
539
+ * @name fillRoundRect
540
+ * @memberof CanvasRenderer.prototype
541
+ * @function
542
+ * @param {number} x
543
+ * @param {number} y
544
+ * @param {number} width
545
+ * @param {number} height
546
+ * @param {number} radius
547
+ */
548
+ fillRoundRect(x, y, width, height, radius) {
549
+ this.strokeRoundRect(x, y, width, height, radius, true);
519
550
  }
520
551
 
521
552
 
@@ -557,7 +588,7 @@ class CanvasRenderer extends Renderer {
557
588
  */
558
589
  restore() {
559
590
  this.backBufferContext2D.restore();
560
- this.currentColor.glArray[3] = this.backBufferContext2D.globalAlpha;
591
+ this.currentColor.glArray[3] = this.getGlobalAlpha();
561
592
  this.currentScissor[0] = 0;
562
593
  this.currentScissor[1] = 0;
563
594
  this.currentScissor[2] = this.backBufferCanvas.width;
@@ -605,7 +636,7 @@ class CanvasRenderer extends Renderer {
605
636
  }
606
637
 
607
638
  /**
608
- * Set the global alpha on the canvas context
639
+ * Set the global alpha
609
640
  * @name setGlobalAlpha
610
641
  * @memberof CanvasRenderer.prototype
611
642
  * @function
@@ -615,6 +646,17 @@ class CanvasRenderer extends Renderer {
615
646
  this.backBufferContext2D.globalAlpha = this.currentColor.glArray[3] = alpha;
616
647
  }
617
648
 
649
+ /**
650
+ * Return the global alpha
651
+ * @name getGlobalAlpha
652
+ * @memberof CanvasRenderer.prototype
653
+ * @function
654
+ * @returns {number} global alpha value
655
+ */
656
+ getGlobalAlpha() {
657
+ return this.backBufferContext2D.globalAlpha;
658
+ }
659
+
618
660
  /**
619
661
  * Set the line width on the context
620
662
  * @name setLineWidth
@@ -721,14 +763,16 @@ class CanvasRenderer extends Renderer {
721
763
  * @name setMask
722
764
  * @memberof CanvasRenderer.prototype
723
765
  * @function
724
- * @param {Rect|Polygon|Line|Ellipse} [mask] the shape defining the mask to be applied
766
+ * @param {Rect|RoundRect|Polygon|Line|Ellipse} [mask] the shape defining the mask to be applied
725
767
  */
726
768
  setMask(mask) {
727
- var context = this.backBufferContext2D;
769
+ var context = this.getContext();
728
770
  var _x = mask.pos.x, _y = mask.pos.y;
729
771
 
730
772
  context.save();
731
773
 
774
+ context.beginPath();
775
+
732
776
  // https://github.com/melonjs/melonJS/issues/648
733
777
  if (mask instanceof Ellipse) {
734
778
  var hw = mask.radiusV.x,
@@ -745,14 +789,14 @@ class CanvasRenderer extends Renderer {
745
789
  ymin = _y - ymagic,
746
790
  ymax = _y + ymagic;
747
791
 
748
- context.beginPath();
749
792
  context.moveTo(_x, ty);
750
793
  context.bezierCurveTo(xmax, ty, rx, ymin, rx, _y);
751
794
  context.bezierCurveTo(rx, ymax, xmax, by, _x, by);
752
795
  context.bezierCurveTo(xmin, by, lx, ymax, lx, _y);
753
796
  context.bezierCurveTo(lx, ymin, xmin, ty, _x, ty);
797
+ } else if (mask instanceof RoundRect) {
798
+ context.roundRect(_x, _y, mask.width, mask.height, mask.radius);
754
799
  } else {
755
- context.beginPath();
756
800
  context.moveTo(_x + mask.points[0].x, _y + mask.points[0].y);
757
801
  var point;
758
802
  for (var i = 1; i < mask.points.length; i++) {
@@ -772,7 +816,7 @@ class CanvasRenderer extends Renderer {
772
816
  * @function
773
817
  */
774
818
  clearMask() {
775
- this.backBufferContext2D.restore();
819
+ this.getContext().restore();
776
820
  }
777
821
 
778
822
  };