diff --git a/src/lime/_internal/backend/html5/HTML5HTTPRequest.hx b/src/lime/_internal/backend/html5/HTML5HTTPRequest.hx
index a9d83663a..5bc0cd5e8 100644
--- a/src/lime/_internal/backend/html5/HTML5HTTPRequest.hx
+++ b/src/lime/_internal/backend/html5/HTML5HTTPRequest.hx
@@ -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
+ {
+ var uri = __createBlobURIFromBytes(bytes, type);
+ if (uri != null)
+ {
+ var promise = new Promise();
+
+ 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
{
var promise = new Promise();
@@ -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):Void
+ private static function __loadImage(uri:String, promise:Promise, 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;
}
diff --git a/src/lime/graphics/Image.hx b/src/lime/graphics/Image.hx
index aad56c223..00074bd75 100644
--- a/src/lime/graphics/Image.hx
+++ b/src/lime/graphics/Image.hx
@@ -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();