Browse Source

rewrote BitmapData as a class

Nicolas Cannasse 10 năm trước cách đây
mục cha
commit
9934c8712c
2 tập tin đã thay đổi với 161 bổ sung156 xóa
  1. 158 150
      hxd/BitmapData.hx
  2. 3 6
      hxd/res/LoadedBitmap.hx

+ 158 - 150
hxd/BitmapData.hx

@@ -1,17 +1,15 @@
 package hxd;
 
-private typedef InnerData =
-#if (flash||openfl)
-	flash.display.BitmapData
+typedef BitmapInnerData =
+#if (flash || openfl || nme)
+	flash.display.BitmapData;
 #elseif js
-	js.html.CanvasRenderingContext2D
-#elseif nme
-	nme.display.BitmapData
+	js.html.CanvasRenderingContext2D;
 #else
-	Int
-#end;
+	Int;
+#end
 
-abstract BitmapData(InnerData) {
+class BitmapData {
 
 	#if (flash || nme || openfl)
 	static var tmpRect = new flash.geom.Rectangle();
@@ -19,28 +17,46 @@ abstract BitmapData(InnerData) {
 	static var tmpMatrix = new flash.geom.Matrix();
 	#end
 
+#if (flash||openfl||nme)
+	var bmp : flash.display.BitmapData;
+#elseif js
+	var ctx : js.html.CanvasRenderingContext2D;
+	var lockImage : js.html.ImageData;
+	var pixel : js.html.ImageData;
+#end
+
 	public var width(get, never) : Int;
 	public var height(get, never) : Int;
 
-	public inline function new(width:Int, height:Int) {
-		#if (flash||openfl||nme)
-		this = new flash.display.BitmapData(width, height, true, 0);
-		#else
-		var canvas = js.Browser.document.createCanvasElement();
-		canvas.width = width;
-		canvas.height = height;
-		this = canvas.getContext2d();
-		#end
+	public function new(width:Int, height:Int) {
+		if( width == -101 && height == -102 ) {
+			// no alloc
+		} else {
+			#if (flash||openfl||nme)
+			bmp = new flash.display.BitmapData(width, height, true, 0);
+			#elseif js
+			var canvas = js.Browser.document.createCanvasElement();
+			canvas.width = width;
+			canvas.height = height;
+			ctx = canvas.getContext2d();
+			#else
+			notImplemented();
+			#end
+		}
 	}
 
-	public inline function clear( color : Int ) {
-		#if (flash||openfl)
-		this.fillRect(this.rect, color);
+	public function clear( color : Int ) {
+		#if (flash||openfl||nme)
+		bmp.fillRect(bmp.rect, color);
 		#else
 		fill(0, 0, width, height, color);
 		#end
 	}
 
+	static inline function notImplemented() {
+		throw "Not implemented";
+	}
+
 	public function fill( x : Int, y : Int, width : Int, height : Int, color : Int ) {
 		#if (flash || openfl || nme)
 		var r = tmpRect;
@@ -48,15 +64,17 @@ abstract BitmapData(InnerData) {
 		r.y = y;
 		r.width = width;
 		r.height = height;
-		this.fillRect(r, color);
+		bmp.fillRect(r, color);
+		#elseif js
+		ctx.fillStyle = 'rgba(${(color>>16)&0xFF}, ${(color>>8)&0xFF}, ${color&0xFF}, ${(color>>>24)/255})';
+		ctx.fillRect(x, y, width, height);
 		#else
-		this.fillStyle = 'rgba(${(color>>16)&0xFF}, ${(color>>8)&0xFF}, ${color&0xFF}, ${(color>>>24)/255})';
-		this.fillRect(x, y, width, height);
+		notImplemented();
 		#end
 	}
 
 	public function draw( x : Int, y : Int, src : BitmapData, srcX : Int, srcY : Int, width : Int, height : Int, ?blendMode : h2d.BlendMode ) {
-		#if flash
+		#if (flash || openfl || nme)
 		if( blendMode == null ) blendMode = Alpha;
 		var r = tmpRect;
 		r.x = srcX;
@@ -68,51 +86,51 @@ abstract BitmapData(InnerData) {
 			var p = tmpPoint;
 			p.x = x;
 			p.y = y;
-			this.copyPixels(src.toNative(), r, p);
+			bmp.copyPixels(src.bmp, r, p);
 		case Alpha:
 			var p = tmpPoint;
 			p.x = x;
 			p.y = y;
-			this.copyPixels(src.toNative(), r, p, src.toNative(), null, true);
+			bmp.copyPixels(src.bmp, r, p, src.bmp, null, true);
 		case Add:
 			var m = tmpMatrix;
 			m.tx = x - srcX;
 			m.ty = y - srcY;
 			r.x = x;
 			r.y = y;
-			this.draw(src.toNative(), m, null, flash.display.BlendMode.ADD, r, false);
+			bmp.draw(src.bmp, m, null, flash.display.BlendMode.ADD, r, false);
 		case Erase:
 			var m = tmpMatrix;
 			m.tx = x - srcX;
 			m.ty = y - srcY;
 			r.x = x;
 			r.y = y;
-			this.draw(src.toNative(), m, null, flash.display.BlendMode.ERASE, r, false);
+			bmp.draw(src.bmp, m, null, flash.display.BlendMode.ERASE, r, false);
 		case Multiply:
 			var m = tmpMatrix;
 			m.tx = x - srcX;
 			m.ty = y - srcY;
 			r.x = x;
 			r.y = y;
-			this.draw(src.toNative(), m, null, flash.display.BlendMode.MULTIPLY, r, false);
+			bmp.draw(src.bmp, m, null, flash.display.BlendMode.MULTIPLY, r, false);
 		case Screen:
 			var m = tmpMatrix;
 			m.tx = x - srcX;
 			m.ty = y - srcY;
 			r.x = x;
 			r.y = y;
-			this.draw(src.toNative(), m, null, flash.display.BlendMode.SCREEN, r, false);
+			bmp.draw(src.bmp, m, null, flash.display.BlendMode.SCREEN, r, false);
 		case SoftAdd:
 			throw "BlendMode not supported";
 		}
 		#else
-		throw "TODO";
+		notImplemented();
 		#end
 	}
 
 	public function drawScaled( x : Int, y : Int, width : Int, height : Int, src : BitmapData, srcX : Int, srcY : Int, srcWidth : Int, srcHeight : Int, ?blendMode : h2d.BlendMode, smooth = true ) {
-		#if flash
 		if( blendMode == null ) blendMode = Alpha;
+		#if (flash || openfl || nme)
 
 		var b = switch( blendMode ) {
 		case None:
@@ -144,12 +162,12 @@ abstract BitmapData(InnerData) {
 		r.width = width;
 		r.height = height;
 
-		this.draw(src.toNative(), m, null, b, r, smooth);
+		bmp.draw(src.bmp, m, null, b, r, smooth);
 		m.a = 1;
 		m.d = 1;
 
 		#else
-		throw "TODO";
+		notImplemented();
 		#end
 	}
 
@@ -173,13 +191,13 @@ abstract BitmapData(InnerData) {
 			for( x in x0...x1 + 1 )
 				setPixel(x, y0, color);
 		} else {
-			throw "TODO";
+			throw "TODO : brensenham line";
 		}
 	}
 
 	public inline function dispose() {
-		#if (flash||openfl)
-		this.dispose();
+		#if (flash||openfl||nme)
+		bmp.dispose();
 		#end
 	}
 
@@ -188,12 +206,12 @@ abstract BitmapData(InnerData) {
 	}
 
 	public function sub( x, y, w, h ) : BitmapData {
-		#if flash
+		#if (flash || openfl || nme)
 		var b = new flash.display.BitmapData(w, h);
-		b.copyPixels(this, new flash.geom.Rectangle(x, y, w, h), new flash.geom.Point(0, 0));
+		b.copyPixels(bmp, new flash.geom.Rectangle(x, y, w, h), new flash.geom.Point(0, 0));
 		return fromNative(b);
 		#else
-		throw "TODO";
+		notImplemented();
 		return null;
 		#end
 	}
@@ -201,35 +219,47 @@ abstract BitmapData(InnerData) {
 	/**
 		Inform that we will perform several pixel operations on the BitmapData.
 	**/
-	public inline function lock() {
+	public function lock() {
 		#if flash
-		this.lock();
+		bmp.lock();
 		#elseif js
-		canvasLock(this, true);
+		if( lockImage == null )
+			lockImage = ctx.getImageData(0, 0, width, height);
 		#end
 	}
 
 	/**
 		Inform that we have finished performing pixel operations on the BitmapData.
 	**/
-	public inline function unlock() {
+	public function unlock() {
 		#if flash
-		this.unlock();
+		bmp.unlock();
 		#elseif js
-		canvasLock(this, false);
+		if( lockImage != null ) {
+			ctx.putImageData(lockImage, 0, 0);
+			lockImage = null;
+		}
 		#end
 	}
 
 	/**
 		Access the pixel color value at the given position. Note : this function can be very slow if done many times and the BitmapData has not been locked.
 	**/
-	public inline function getPixel( x : Int, y : Int ) : Int {
-		#if ( flash || openfl )
-		return this.getPixel32(x, y);
+	public #if (flash || openfl || nme) inline #end function getPixel( x : Int, y : Int ) : Int {
+		#if ( flash || openfl || nme )
+		return bmp.getPixel32(x, y);
 		#elseif js
-		return canvasGetPixel(this, x, y);
+		var i = lockImage;
+		var a;
+		if( i != null )
+			a = (x + y * i.width) << 2;
+		else {
+			a = 0;
+			i = ctx.getImageData(x, y, 1, 1);
+		}
+		return (i.data[a] << 16) | (i.data[a|1] << 8) | i.data[a|2] | (i.data[a|3] << 24);
 		#else
-		throw "TODO";
+		notImplemented();
 		return 0;
 		#end
 	}
@@ -237,64 +267,65 @@ abstract BitmapData(InnerData) {
 	/**
 		Modify the pixel color value at the given position. Note : this function can be very slow if done many times and the BitmapData has not been locked.
 	**/
-	public inline function setPixel( x : Int, y : Int, c : Int ) {
-		#if (flash||openfl)
-		this.setPixel32(x, y, c);
+	public #if (flash || openfl || nme) inline #end function setPixel( x : Int, y : Int, c : Int ) {
+		#if ( flash || openfl || nme)
+		bmp.setPixel32(x, y, c);
 		#elseif js
-		canvasSetPixel(this, x, y, c);
+		var i : js.html.ImageData = lockImage;
+		if( i != null ) {
+			var a = (x + y * i.width) << 2;
+			i.data[a] = (c >> 16) & 0xFF;
+			i.data[a|1] = (c >> 8) & 0xFF;
+			i.data[a|2] = c & 0xFF;
+			i.data[a|3] = (c >>> 24) & 0xFF;
+			return;
+		}
+		var i = pixel;
+		if( i == null ) {
+			i = ctx.createImageData(1, 1);
+			pixel = i;
+		}
+		i.data[0] = (c >> 16) & 0xFF;
+		i.data[1] = (c >> 8) & 0xFF;
+		i.data[2] = c & 0xFF;
+		i.data[3] = (c >>> 24) & 0xFF;
+		ctx.putImageData(i, x, y);
 		#else
-		throw "TODO";
+		notImplemented();
 		#end
 	}
 
 	inline function get_width() : Int {
-		#if js
-		return this.canvas.width;
+		#if (flash || nme || openfl)
+		return bmp.width;
+		#elseif js
+		return ctx.canvas.width;
 		#else
-		return this.width;
+		notImplemented();
+		return 0;
 		#end
 	}
 
 	inline function get_height() {
-		#if js
-		return this.canvas.height;
+		#if (flash || nme || openfl)
+		return bmp.height;
+		#elseif js
+		return ctx.canvas.height;
 		#else
-		return this.height;
+		notImplemented();
+		return 0;
 		#end
 	}
 
-	public inline function getPixels() : Pixels {
-		return nativeGetPixels(this);
-	}
-
-	public inline function setPixels( pixels : Pixels ) {
-		nativeSetPixels(this, pixels);
-	}
-
-	public inline function toNative() : InnerData {
-		return this;
-	}
-
-	public static inline function fromNative( bmp : InnerData ) : BitmapData {
-		return cast bmp;
-	}
-
-	public function toPNG() {
-		var pixels = getPixels();
-		var png = pixels.toPNG();
-		pixels.dispose();
-		return png;
-	}
-
-	static function nativeGetPixels( b : InnerData ) {
-		#if flash
-		var p = new Pixels(b.width, b.height, haxe.io.Bytes.ofData(b.getPixels(b.rect)), ARGB);
+	public function getPixels() : Pixels {
+		#if (flash || nme || openfl)
+		var p = new Pixels(width, height, haxe.io.Bytes.ofData(bmp.getPixels(bmp.rect)), ARGB);
 		p.flags.set(AlphaPremultiplied);
 		return p;
 		#elseif js
-		var w = b.canvas.width;
-		var h = b.canvas.height;
-		var data = b.getImageData(0, 0, w, h).data;
+		var w = width;
+		var h = height;
+		var data = ctx.getImageData(0, 0, w, h).data;
 			#if (haxe_ver < 3.2)
 			var pixels = [];
 			for( i in 0...w * h * 4 )
@@ -303,20 +334,14 @@ abstract BitmapData(InnerData) {
 			// starting from Haxe 3.2, bytes are based on native array
 			var pixels = data;
 			#end
-		return new Pixels(b.canvas.width, b.canvas.height, haxe.io.Bytes.ofData(pixels), RGBA);
-		#elseif openfl
-		var bRect = b.rect;
-		var bPixels : haxe.io.Bytes = hxd.ByteConversions.byteArrayToBytes(b.getPixels(b.rect));
-		var p = new Pixels(b.width, b.height, bPixels, ARGB);
-		p.flags.set(AlphaPremultiplied);
-		return p;
+		return new Pixels(w, h, haxe.io.Bytes.ofData(pixels), RGBA);
 		#else
-		throw "TODO";
+		notImplemented();
 		return null;
 		#end
 	}
 
-	static function nativeSetPixels( b : InnerData, pixels : Pixels ) {
+	public function setPixels( pixels : Pixels ) {
 		#if flash
 		var bytes = pixels.bytes.getData();
 		bytes.position = 0;
@@ -329,65 +354,48 @@ abstract BitmapData(InnerData) {
 			pixels.convert(BGRA);
 			bytes.endian = flash.utils.Endian.LITTLE_ENDIAN;
 		}
-		b.setPixels(b.rect, bytes);
+		bmp.setPixels(bmp.rect, bytes);
 		#elseif js
-		var img = b.createImageData(pixels.width, pixels.height);
+		var img = ctx.createImageData(pixels.width, pixels.height);
 		pixels.convert(RGBA);
 		for( i in 0...pixels.width*pixels.height*4 ) img.data[i] = pixels.bytes.get(i);
-		b.putImageData(img, 0, 0);
-		#elseif cpp
-		b.setPixels(b.rect, flash.utils.ByteArray.fromBytes(pixels.bytes));
+		ctx.putImageData(img, 0, 0);
+		#elseif (nme || openfl)
+		pixels.convert(BGRA);
+		bmp.setPixels(bmp.rect, flash.utils.ByteArray.fromBytes(pixels.bytes));
 		#else
-		throw "TODO";
+		notImplemented();
 		#end
 	}
 
-	#if js
-
-	static function canvasLock( b : InnerData, lock : Bool ) untyped {
-		if( lock ) {
-			if( b.lockImage == null )
-				b.lockImage = b.getImageData(0, 0, b.canvas.width, b.canvas.height);
-		} else {
-			if( b.lockImage != null ) {
-				b.putImageData(b.lockImage, 0, 0);
-				b.lockImage = null;
-			}
-		}
+	public inline function toNative() : BitmapInnerData {
+		#if (flash || nme || openfl)
+		return bmp;
+		#elseif js
+		return ctx;
+		#else
+		notImplemented();
+		return 0;
+		#end
 	}
 
-	static function canvasGetPixel( b : InnerData, x : Int, y : Int ) {
-		var i : js.html.ImageData = untyped b.lockImage;
-		var a;
-		if( i != null )
-			a = (x + y * i.width) << 2;
-		else {
-			a = 0;
-			i = b.getImageData(x, y, 1, 1);
-		}
-		return (i.data[a] << 16) | (i.data[a|1] << 8) | i.data[a|2] | (i.data[a|3] << 24);
+	public static function fromNative( data : BitmapInnerData ) : BitmapData {
+		var b = new BitmapData( -101, -102 );
+		#if (flash || nme || openfl)
+		b.bmp = data;
+		#elseif js
+		b.ctx = data;
+		#else
+		notImplemented();
+		#end
+		return b;
 	}
 
-	static function canvasSetPixel( b : InnerData, x : Int, y : Int, c : Int ) {
-		var i : js.html.ImageData = untyped b.lockImage;
-		if( i != null ) {
-			var a = (x + y * i.width) << 2;
-			i.data[a] = (c >> 16) & 0xFF;
-			i.data[a|1] = (c >> 8) & 0xFF;
-			i.data[a|2] = c & 0xFF;
-			i.data[a|3] = (c >>> 24) & 0xFF;
-			return;
-		}
-		var i = untyped b.pixel;
-		if( i == null ) {
-			i = b.createImageData(1, 1);
-			untyped b.pixel = i;
-		}
-		i.data[0] = (c >> 16) & 0xFF;
-		i.data[1] = (c >> 8) & 0xFF;
-		i.data[2] = c & 0xFF;
-		i.data[3] = (c >>> 24) & 0xFF;
-		b.putImageData(i, x, y);
+	public function toPNG() {
+		var pixels = getPixels();
+		var png = pixels.toPNG();
+		pixels.dispose();
+		return png;
 	}
-	#end
+
 }

+ 3 - 6
hxd/res/LoadedBitmap.hx

@@ -12,12 +12,9 @@ abstract LoadedBitmap(LoadedBitmapData) {
 		#if flash
 		return hxd.BitmapData.fromNative(this);
 		#elseif js
-		var canvas = js.Browser.document.createCanvasElement();
-		canvas.width = this.width;
-		canvas.height = this.height;
-		var ctx = canvas.getContext2d();
-		ctx.drawImage(this, 0, 0);
-		return hxd.BitmapData.fromNative(ctx);
+		var bmp = new hxd.BitmapData(this.width, this.height);
+		@:privateAccess bmp.ctx.drawImage(this, 0, 0);
+		return bmp;
 		#else
 		throw "TODO";
 		return null;