-
-
Notifications
You must be signed in to change notification settings - Fork 191
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Closes #2359 This PR switches away from `Image.decode()` to use the older method of `Image.onload` this method is more reliable for large or numerous images due to a chromium bug around decode. @HxShard provided a great codesandbox illustrating the issue https://codesandbox.io/s/happy-gagarin-j1vjr ## Changes: - Creates a new Future type for working with native Promises' resolve/reject at any time - Creates a new Semaphore type that can limit async calls in between `enter` and `exit` - Fixes a small clock schedule bug as well
- Loading branch information
Showing
14 changed files
with
451 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Decode Images</title> | ||
</head> | ||
<body> | ||
<p>There should be no errors in the console relating to loading images</p> | ||
<script src="../../lib/excalibur.js"></script> | ||
<script src="./index.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
var game = new ex.Engine({ | ||
width: 800, | ||
height: 600 | ||
}); | ||
|
||
var loader = new ex.Loader(); | ||
|
||
function generate() { | ||
let srcs = []; | ||
for (let i = 0; i < 800; i++) { | ||
srcs.push(generateRandomImage()); | ||
} | ||
let images = srcs.map(src => new ex.ImageSource(src)); | ||
loader.addResources(images); | ||
|
||
let sprites = images.map(i => i.toSprite()); | ||
|
||
game.currentScene.onPostDraw = ctx => { | ||
ctx.save(); | ||
ctx.scale(.25, .25); | ||
for (let i = 0; i < sprites.length; i++) { | ||
sprites[i].draw(ctx, (i * 100) % (800 * 4) + 10, Math.floor((i * 100) / (800 * 4)) * 100 + 10); | ||
} | ||
ctx.restore(); | ||
}; | ||
} | ||
|
||
function drawRandomCircleOnContext(ctx) { | ||
const x = Math.floor(Math.random() * 100); | ||
const y = Math.floor(Math.random() * 100); | ||
const radius = Math.floor(Math.random() * 20); | ||
|
||
const r = Math.floor(Math.random() * 255); | ||
const g = Math.floor(Math.random() * 255); | ||
const b = Math.floor(Math.random() * 255); | ||
|
||
ctx.beginPath(); | ||
ctx.arc(x, y, radius, Math.PI * 2, 0, false); | ||
ctx.fillStyle = "rgba(" + r + "," + g + "," + b + ",1)"; | ||
ctx.fill(); | ||
ctx.closePath(); | ||
} | ||
|
||
function generateRandomImage() { | ||
const canvas = document.createElement("canvas"); | ||
canvas.width = 100; | ||
canvas.height = 100; | ||
|
||
const ctx = canvas.getContext("2d"); | ||
ctx.clearRect(0, 0, 100, 100); | ||
|
||
for (let i = 0; i < 20; i++) { | ||
drawRandomCircleOnContext(ctx); | ||
} | ||
return canvas.toDataURL("image/png"); | ||
} | ||
|
||
|
||
generate(); | ||
|
||
|
||
|
||
game.start(loader); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
|
||
/** | ||
* Future is a wrapper around a native browser Promise to allow resolving/rejecting at any time | ||
*/ | ||
export class Future<T> { | ||
// Code from StephenCleary https://gist.github.com/StephenCleary/ba50b2da419c03b9cba1d20cb4654d5e | ||
private _resolver: (value: T) => void; | ||
private _rejecter: (error: Error) => void; | ||
private _isCompleted: boolean = false; | ||
|
||
constructor() { | ||
this.promise = new Promise((resolve, reject) => { | ||
this._resolver = resolve; | ||
this._rejecter = reject; | ||
}); | ||
} | ||
|
||
public readonly promise: Promise<T>; | ||
|
||
public get isCompleted(): boolean { | ||
return this._isCompleted; | ||
} | ||
|
||
public resolve(value: T): void { | ||
if (this._isCompleted) { | ||
return; | ||
} | ||
this._isCompleted = true; | ||
this._resolver(value); | ||
} | ||
|
||
public reject(error: Error): void { | ||
if (this._isCompleted) { | ||
return; | ||
} | ||
this._isCompleted = true; | ||
this._rejecter(error); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { Future } from './Future'; | ||
|
||
class AsyncWaitQueue<T> { | ||
// Code from StephenCleary https://gist.github.com/StephenCleary/ba50b2da419c03b9cba1d20cb4654d5e | ||
private _queue: Future<T>[] = []; | ||
|
||
public get length(): number { | ||
return this._queue.length; | ||
} | ||
|
||
public enqueue(): Promise<T> { | ||
const future = new Future<T>(); | ||
this._queue.push(future); | ||
return future.promise; | ||
} | ||
|
||
public dequeue(value: T): void { | ||
const future = this._queue.shift(); | ||
future.resolve(value); | ||
} | ||
} | ||
|
||
/** | ||
* Semaphore allows you to limit the amount of async calls happening between `enter()` and `exit()` | ||
* | ||
* This can be useful when limiting the number of http calls, browser api calls, etc either for performance or to work | ||
* around browser limitations like max Image.decode() calls in chromium being 256. | ||
*/ | ||
export class Semaphore { | ||
private _waitQueue = new AsyncWaitQueue(); | ||
constructor(private _count: number) { } | ||
|
||
public get count() { | ||
return this._count; | ||
} | ||
|
||
public get waiting() { | ||
return this._waitQueue.length; | ||
} | ||
|
||
public async enter() { | ||
if (this._count !== 0) { | ||
this._count--; | ||
return Promise.resolve(); | ||
} | ||
return this._waitQueue.enqueue(); | ||
} | ||
|
||
public exit(count: number = 1) { | ||
if (count === 0) { | ||
return; | ||
} | ||
while (count !== 0 && this._waitQueue.length !== 0) { | ||
this._waitQueue.dequeue(null); | ||
count--; | ||
} | ||
this._count += count; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.