Speed up Image loadFromBytes (thanks D-MAN)
This commit is contained in:
@@ -2,13 +2,16 @@ package lime._internal.backend.html5;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import js.html.AnchorElement;
|
||||
import js.html.Blob;
|
||||
import js.html.ErrorEvent;
|
||||
import js.html.Event;
|
||||
import js.html.Image as JSImage;
|
||||
import js.html.ProgressEvent;
|
||||
import js.html.URL;
|
||||
import js.html.XMLHttpRequest;
|
||||
import js.html.XMLHttpRequestResponseType;
|
||||
import js.Browser;
|
||||
import lime._internal.format.Base64;
|
||||
import lime.app.Future;
|
||||
import lime.app.Promise;
|
||||
import lime.graphics.Image;
|
||||
@@ -21,6 +24,8 @@ import lime.utils.AssetType;
|
||||
@:access(lime.graphics.Image)
|
||||
class HTML5HTTPRequest
|
||||
{
|
||||
private static inline var OPTION_REVOKE_URL:Int = 1 << 0;
|
||||
|
||||
private static var activeRequests = 0;
|
||||
private static var originElement:AnchorElement;
|
||||
private static var originHostname:String;
|
||||
@@ -172,7 +177,8 @@ class HTML5HTTPRequest
|
||||
instance: this,
|
||||
uri: uri,
|
||||
promise: promise,
|
||||
type: AssetType.BINARY
|
||||
type: AssetType.BINARY,
|
||||
options: 0
|
||||
});
|
||||
}
|
||||
|
||||
@@ -186,7 +192,7 @@ class HTML5HTTPRequest
|
||||
if (activeRequests < requestLimit)
|
||||
{
|
||||
activeRequests++;
|
||||
__loadImage(uri, promise);
|
||||
__loadImage(uri, promise, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -195,13 +201,46 @@ class HTML5HTTPRequest
|
||||
instance: null,
|
||||
uri: uri,
|
||||
promise: promise,
|
||||
type: AssetType.IMAGE
|
||||
type: AssetType.IMAGE,
|
||||
options: 0
|
||||
});
|
||||
}
|
||||
|
||||
return promise.future;
|
||||
}
|
||||
|
||||
private static function loadImageFromBytes(bytes:Bytes, type:String):Future<Image>
|
||||
{
|
||||
var uri = __createBlobURIFromBytes(bytes, type);
|
||||
if (uri != null)
|
||||
{
|
||||
var promise = new Promise<Image>();
|
||||
|
||||
if (activeRequests < requestLimit)
|
||||
{
|
||||
activeRequests++;
|
||||
__loadImage(uri, promise, OPTION_REVOKE_URL);
|
||||
}
|
||||
else
|
||||
{
|
||||
requestQueue.add(
|
||||
{
|
||||
instance: null,
|
||||
uri: uri,
|
||||
promise: promise,
|
||||
type: AssetType.IMAGE,
|
||||
options: OPTION_REVOKE_URL
|
||||
});
|
||||
}
|
||||
|
||||
return promise.future;
|
||||
}
|
||||
else
|
||||
{
|
||||
return loadImage("data:" + type + ";base64," + Base64.encode(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
public function loadText(uri:String):Future<String>
|
||||
{
|
||||
var promise = new Promise<String>();
|
||||
@@ -218,7 +257,8 @@ class HTML5HTTPRequest
|
||||
instance: this,
|
||||
uri: uri,
|
||||
promise: promise,
|
||||
type: AssetType.TEXT
|
||||
type: AssetType.TEXT,
|
||||
options: 0
|
||||
});
|
||||
}
|
||||
|
||||
@@ -236,7 +276,7 @@ class HTML5HTTPRequest
|
||||
switch (queueItem.type)
|
||||
{
|
||||
case IMAGE:
|
||||
__loadImage(queueItem.uri, queueItem.promise);
|
||||
__loadImage(queueItem.uri, queueItem.promise, queueItem.options);
|
||||
|
||||
case TEXT:
|
||||
queueItem.instance.__loadText(queueItem.uri, queueItem.promise);
|
||||
@@ -272,6 +312,11 @@ class HTML5HTTPRequest
|
||||
parent.responseStatus = request.status;
|
||||
}
|
||||
|
||||
private static inline function __createBlobURIFromBytes(bytes:Bytes, type:String):String
|
||||
{
|
||||
return URL.createObjectURL(new Blob([bytes.getData()], {type: type}));
|
||||
}
|
||||
|
||||
private static function __fixHostname(hostname:String):String
|
||||
{
|
||||
return hostname == null ? "" : hostname;
|
||||
@@ -301,10 +346,15 @@ class HTML5HTTPRequest
|
||||
return (protocol == null || protocol == "") ? "http:" : protocol;
|
||||
}
|
||||
|
||||
private static function __isInMemoryURI(uri:String):Bool
|
||||
{
|
||||
return StringTools.startsWith(uri, "data:") || StringTools.startsWith(uri, "blob:");
|
||||
}
|
||||
|
||||
private static function __isSameOrigin(path:String):Bool
|
||||
{
|
||||
if (path == null || path == "") return true;
|
||||
if (StringTools.startsWith(path, "data:")) return true;
|
||||
if (__isInMemoryURI(path)) return true;
|
||||
|
||||
if (originElement == null)
|
||||
{
|
||||
@@ -380,7 +430,7 @@ class HTML5HTTPRequest
|
||||
load(uri, progress, readyStateChange);
|
||||
}
|
||||
|
||||
private static function __loadImage(uri:String, promise:Promise<Image>):Void
|
||||
private static function __loadImage(uri:String, promise:Promise<Image>, options:Int):Void
|
||||
{
|
||||
var image = new JSImage();
|
||||
|
||||
@@ -394,10 +444,11 @@ class HTML5HTTPRequest
|
||||
supportsImageProgress = untyped __js__("'onprogress' in image");
|
||||
}
|
||||
|
||||
if (supportsImageProgress || StringTools.startsWith(uri, "data:"))
|
||||
if (supportsImageProgress || __isInMemoryURI(uri))
|
||||
{
|
||||
image.addEventListener("load", function(event)
|
||||
{
|
||||
__revokeBlobURI(uri, options);
|
||||
var buffer = new ImageBuffer(null, image.width, image.height);
|
||||
buffer.__srcImage = cast image;
|
||||
|
||||
@@ -414,6 +465,8 @@ class HTML5HTTPRequest
|
||||
|
||||
image.addEventListener("error", function(event)
|
||||
{
|
||||
__revokeBlobURI(uri, options);
|
||||
|
||||
activeRequests--;
|
||||
processQueue();
|
||||
|
||||
@@ -489,6 +542,14 @@ class HTML5HTTPRequest
|
||||
binary = false;
|
||||
load(uri, progress, readyStateChange);
|
||||
}
|
||||
|
||||
private static function __revokeBlobURI(uri:String, options:Int):Void
|
||||
{
|
||||
if ((options & OPTION_REVOKE_URL) != 0)
|
||||
{
|
||||
URL.revokeObjectURL(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@:dox(hide) typedef QueueItem =
|
||||
@@ -497,4 +558,5 @@ class HTML5HTTPRequest
|
||||
var type:AssetType;
|
||||
var promise:Dynamic;
|
||||
var uri:String;
|
||||
var options:Int;
|
||||
}
|
||||
|
||||
@@ -387,7 +387,8 @@ class Image
|
||||
sourceRect.offset(sourceImage.offsetX, sourceImage.offsetY);
|
||||
destPoint.offset(offsetX, offsetY);
|
||||
|
||||
buffer.__srcBitmapData.copyChannel(sourceImage.buffer.src, sourceRect.__toFlashRectangle(), destPoint.__toFlashPoint(), srcChannel, dstChannel);
|
||||
buffer.__srcBitmapData.copyChannel(sourceImage.buffer.src, sourceRect.__toFlashRectangle(), destPoint.__toFlashPoint(), srcChannel,
|
||||
dstChannel);
|
||||
|
||||
default:
|
||||
}
|
||||
@@ -616,20 +617,16 @@ class Image
|
||||
@param bitmapData A source `bitmapData` to use
|
||||
@return A new `Image` instance
|
||||
**/
|
||||
#if flash
|
||||
public static function fromBitmapData(bitmapData:BitmapData):Image {
|
||||
#else
|
||||
public static function fromBitmapData(bitmapData:Dynamic):Image {
|
||||
#end
|
||||
if (bitmapData == null) return null;
|
||||
#if flash
|
||||
var buffer = new ImageBuffer(null, bitmapData.width, bitmapData.height);
|
||||
|
||||
buffer.__srcBitmapData = bitmapData;
|
||||
return new Image(buffer);
|
||||
#else
|
||||
return bitmapData.image;
|
||||
#end
|
||||
public static function fromBitmapData(bitmapData:#if flash BitmapData #else Dynamic #end):Image
|
||||
{
|
||||
if (bitmapData == null) return null;
|
||||
#if flash
|
||||
var buffer = new ImageBuffer(null, bitmapData.width, bitmapData.height);
|
||||
buffer.__srcBitmapData = bitmapData;
|
||||
return new Image(buffer);
|
||||
#else
|
||||
return bitmapData.image;
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -664,18 +661,15 @@ class Image
|
||||
@param canvas A `CanvasElement`
|
||||
@return A new `Image` instance
|
||||
**/
|
||||
#if (js && html5)
|
||||
public static function fromCanvas(canvas:CanvasElement):Image {
|
||||
#else
|
||||
public static function fromCanvas(canvas:Dynamic):Image {
|
||||
#end
|
||||
if (canvas == null) return null;
|
||||
var buffer = new ImageBuffer(null, canvas.width, canvas.height);
|
||||
buffer.src = canvas;
|
||||
var image = new Image(buffer);
|
||||
public static function fromCanvas(canvas:#if (js && html5) CanvasElement #else Dynamic #end):Image
|
||||
{
|
||||
if (canvas == null) return null;
|
||||
var buffer = new ImageBuffer(null, canvas.width, canvas.height);
|
||||
buffer.src = canvas;
|
||||
var image = new Image(buffer);
|
||||
|
||||
image.type = CANVAS;
|
||||
return image;
|
||||
image.type = CANVAS;
|
||||
return image;
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -710,18 +704,15 @@ class Image
|
||||
@param image An `ImageElement` instance
|
||||
@return A new `Image` instance
|
||||
**/
|
||||
#if (js && html5)
|
||||
public static function fromImageElement(image:ImageElement):Image {
|
||||
#else
|
||||
public static function fromImageElement(image:Dynamic):Image {
|
||||
#end
|
||||
if (image == null) return null;
|
||||
var buffer = new ImageBuffer(null, image.width, image.height);
|
||||
buffer.src = image;
|
||||
var _image = new Image(buffer);
|
||||
public static function fromImageElement(image:#if (js && html5) ImageElement #else Dynamic #end):Image
|
||||
{
|
||||
if (image == null) return null;
|
||||
var buffer = new ImageBuffer(null, image.width, image.height);
|
||||
buffer.src = image;
|
||||
var _image = new Image(buffer);
|
||||
|
||||
_image.type = CANVAS;
|
||||
return _image;
|
||||
_image.type = CANVAS;
|
||||
return _image;
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -967,8 +958,11 @@ class Image
|
||||
// throw "Image tried to read PNG/JPG Bytes, but found an invalid header.";
|
||||
return Future.withValue(null);
|
||||
}
|
||||
|
||||
#if !display
|
||||
return HTML5HTTPRequest.loadImageFromBytes(bytes, type);
|
||||
#else
|
||||
return loadFromBase64(Base64.encode(bytes), type);
|
||||
#end
|
||||
#elseif flash
|
||||
var promise = new Promise<Image>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user