Speed up Image loadFromBytes (thanks D-MAN)

This commit is contained in:
Joshua Granick
2019-07-11 09:19:31 -07:00
parent 3de9f59091
commit 66344c8523
2 changed files with 102 additions and 46 deletions

View File

@@ -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;
}

View File

@@ -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>();