Throttle HTML5 image loading

This commit is contained in:
Joshua Granick
2017-03-21 16:25:32 -07:00
parent ec49da5010
commit d593685dcb
2 changed files with 177 additions and 140 deletions

View File

@@ -2,21 +2,27 @@ package lime._backend.html5;
import js.html.Event;
import js.html.Image in JSImage;
import js.html.ProgressEvent;
import js.html.XMLHttpRequest;
import haxe.io.Bytes;
import lime.app.Future;
import lime.app.Promise;
import lime.graphics.Image;
import lime.graphics.ImageBuffer;
import lime.net.HTTPRequest;
import lime.net.HTTPRequestHeader;
import lime.utils.AssetType;
@:access(lime.graphics.ImageBuffer)
class HTML5HTTPRequest {
private static var activeRequests:Int;
private static var requestLimit:Int;
private static var requestQueue:List<QueueItem>;
private static var activeRequests = 0;
private static var requestLimit = 4;
private static var requestQueue = new List<QueueItem> ();
private var binary:Bool;
private var parent:_IHTTPRequest;
@@ -45,14 +51,6 @@ class HTML5HTTPRequest {
this.parent = parent;
if (requestQueue == null) {
activeRequests = 0;
requestLimit = 6;
requestQueue = new List<QueueItem> ();
}
}
@@ -134,6 +132,124 @@ class HTML5HTTPRequest {
var promise = new Promise<Bytes> ();
if (activeRequests < requestLimit) {
activeRequests++;
__loadData (uri, promise);
} else {
requestQueue.add ({ instance: this, uri: uri, promise: promise, type: AssetType.BINARY });
}
return promise.future;
}
private static function loadImage (uri:String):Future<Image> {
var promise = new Promise<Image> ();
if (activeRequests < requestLimit) {
activeRequests++;
__loadImage (uri, promise);
} else {
requestQueue.add ({ instance: null, uri: uri, promise: promise, type: AssetType.IMAGE });
}
return promise.future;
}
public function loadText (uri:String):Future<String> {
var promise = new Promise<String> ();
if (activeRequests < requestLimit) {
activeRequests++;
__loadText (uri, promise);
} else {
requestQueue.add ({ instance: this, uri: uri, promise: promise, type: AssetType.TEXT });
}
return promise.future;
}
private static function processQueue ():Void {
if (activeRequests < requestLimit && requestQueue.length > 0) {
activeRequests++;
var queueItem = requestQueue.pop ();
switch (queueItem.type) {
case IMAGE:
__loadImage (queueItem.uri, queueItem.promise);
case TEXT:
queueItem.instance.__loadText (queueItem.uri, queueItem.promise);
case BINARY:
queueItem.instance.__loadData (queueItem.uri, queueItem.promise);
default:
activeRequests--;
}
}
}
private function processResponse ():Void {
if (parent.enableResponseHeaders) {
parent.responseHeaders = [];
var name, value;
for (line in request.getAllResponseHeaders ().split ("\n")) {
name = StringTools.trim (line.substr (0, line.indexOf (":")));
value = StringTools.trim (line.substr (line.indexOf (":") + 1));
if (name != "") {
parent.responseHeaders.push (new HTTPRequestHeader (name, value));
}
}
}
parent.responseStatus = request.status;
}
public function __loadData (uri:String, promise:Promise<Bytes>):Void {
var progress = function (event) {
promise.progress (event.loaded, event.total);
@@ -176,26 +292,49 @@ class HTML5HTTPRequest {
}
binary = true;
if (activeRequests < requestLimit) {
activeRequests++;
load (uri, progress, readyStateChange);
} else {
requestQueue.add ({ instance: this, uri: uri, progress: progress, readyStateChange: readyStateChange });
}
return promise.future;
private static function __loadImage (uri:String, promise:Promise<Image>):Void {
var image = new JSImage ();
image.crossOrigin = "Anonymous";
image.addEventListener ("load", function (event) {
var buffer = new ImageBuffer (null, image.width, image.height);
buffer.__srcImage = cast image;
activeRequests--;
processQueue ();
promise.complete (new Image (buffer));
}, false);
image.addEventListener ("progress", function (event) {
promise.progress (event.loaded, event.total);
}, false);
image.addEventListener ("error", function (event) {
activeRequests--;
processQueue ();
promise.error (event.detail);
}, false);
image.src = uri;
}
public function loadText (uri:String):Future<String> {
var promise = new Promise<String> ();
private function __loadText (uri:String, promise:Promise<String>):Void {
var progress = function (event) {
@@ -227,72 +366,19 @@ class HTML5HTTPRequest {
}
binary = false;
if (activeRequests < requestLimit) {
activeRequests++;
load (uri, progress, readyStateChange);
} else {
requestQueue.add ({ instance: this, uri: uri, progress: progress, readyStateChange: readyStateChange });
}
return promise.future;
}
private function processQueue ():Void {
if (activeRequests < requestLimit && requestQueue.length > 0) {
activeRequests++;
var queueItem = requestQueue.pop ();
queueItem.instance.load (queueItem.uri, queueItem.progress, queueItem.readyStateChange);
}
}
private function processResponse ():Void {
if (parent.enableResponseHeaders) {
parent.responseHeaders = [];
var name, value;
for (line in request.getAllResponseHeaders ().split ("\n")) {
name = StringTools.trim (line.substr (0, line.indexOf (":")));
value = StringTools.trim (line.substr (line.indexOf (":") + 1));
if (name != "") {
parent.responseHeaders.push (new HTTPRequestHeader (name, value));
}
}
}
parent.responseStatus = request.status;
}
}
@:dox(hide) private typedef QueueItem = {
@:dox(hide) typedef QueueItem = {
var instance:HTML5HTTPRequest;
var type:AssetType;
var promise:Dynamic;
var uri:String;
var progress:Dynamic;
var readyStateChange:Dynamic;
}

View File

@@ -27,6 +27,7 @@ import lime.utils.ArrayBuffer;
import lime.utils.UInt8Array;
#if (js && html5)
import lime._backend.html5.HTML5HTTPRequest;
import js.html.CanvasElement;
import js.html.ImageElement;
import js.html.Image in JSImage;
@@ -73,6 +74,10 @@ import lime.graphics.console.TextureData;
@:access(lime.math.Rectangle)
@:access(lime.math.Vector2)
#if (js && html5)
@:access(lime._backend.html5.HTML5HTTPRequest)
#end
class Image {
@@ -744,42 +749,16 @@ class Image {
if (base64 == null || type == null) return Future.withValue (null);
var promise = new Promise<Image> ();
#if (js && html5)
var image = new JSImage ();
image.addEventListener ("load", function (event) {
var buffer = new ImageBuffer (null, image.width, image.height);
buffer.__srcImage = cast image;
promise.complete (new Image (buffer));
}, false);
image.addEventListener ("progress", function (event) {
promise.progress (event.loaded, event.total);
}, false);
image.addEventListener ("error", function (event) {
promise.error (event.detail);
}, false);
image.src = "data:" + type + ";base64," + base64;
return HTML5HTTPRequest.loadImage ("data:" + type + ";base64," + base64);
#else
promise.error ("");
return cast Future.withError ("");
#end
return promise.future;
}
@@ -854,35 +833,7 @@ class Image {
#if (js && html5)
var promise = new Promise<Image> ();
var image = new JSImage ();
image.crossOrigin = "Anonymous";
image.addEventListener ("load", function (event) {
var buffer = new ImageBuffer (null, image.width, image.height);
buffer.__srcImage = cast image;
promise.complete (new Image (buffer));
}, false);
image.addEventListener ("progress", function (event) {
promise.progress (event.loaded, event.total);
}, false);
image.addEventListener ("error", function (event) {
promise.error (event.detail);
}, false);
image.src = path;
return promise.future;
return HTML5HTTPRequest.loadImage (path);
#elseif flash