123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- package h2d;
- /**
- A core 2D rendering component representing a region of an underlying `h3d.mat.Texture`.
- Tiles cannot be created directly, and instances are created with the following methods:
- * Via the Resource Management system: `hxd.res.Image.toTile`.
- * From pre-existing Texture: `Tile.fromTexture`.
- * From pre-existing `BitmapData` or `Pixels`: `Tile.fromBitmap` and `Tile.fromPixels` (as well as `Tile.autoCut`).
- * From solid color: `Tile.fromColor`.
- * From previously existing Tile instance via various methods, such as `Tile.sub`.
- **/
- @:allow(h2d)
- class Tile {
- var innerTex : h3d.mat.Texture;
- var u : Float;
- var v : Float;
- var u2 : Float;
- var v2 : Float;
- /**
- Visual offset of the Tile along the X axis during rendering.
- **/
- public var dx : Float;
- /**
- Visual offset of the Tile along the Y axis during rendering.
- **/
- public var dy : Float;
- /**
- Horizontal position of the Tile on the Texture.
- Cannot be modified directly, use `Tile.setPosition` instead.
- **/
- public var x(default,null) : Float;
- /**
- Vertical position of the Tile on the Texture.
- Cannot be modified directly, use `Tile.setPosition` instead.
- **/
- public var y(default,null) : Float;
- /**
- Width of the Tile.
- Not guaranteed to represent real width of the Tile on texture. (see `Tile.scaleToSize`)
- Cannot be modified directly, use `Tile.setSize` instead.
- **/
- public var width(default,null) : Float;
- /**
- Height of the Tile.
- Not guaranteed to represent real height of the Tile on texture. (see `Tile.scaleToSize`)
- Cannot be modified directly, use `Tile.setSize` instead.
- **/
- public var height(default,null) : Float;
- /**
- The flip state of the Tile.
- @see `Tile.flipX`
- **/
- public var xFlip(get,set) : Bool;
- /**
- The flip state of the Tile.
- @see `Tile.flipY`
- **/
- public var yFlip(get,set) : Bool;
- /**
- An integer horizontal position of the Tile on the Texture.
- Alias to `Math.floor(tile.x)`.
- **/
- public var ix(get,never) : Int;
- inline function get_ix() return Math.floor(x);
- /**
- An integer vertical position of the Tile on the Texture.
- Alias to `Math.floor(tile.y)`.
- **/
- public var iy(get,never) : Int;
- inline function get_iy() return Math.floor(y);
- /**
- An integer width of the Tile.
- Alias to `Math.ceil(tile.width + tile.x) - tile.ix`.
- **/
- public var iwidth(get,never) : Int;
- inline function get_iwidth() return Math.ceil(width + x) - ix;
- /**
- An integer height of the Tile.
- Alias to `Math.ceil(tile.height + tile.y) - tile.iy`.
- **/
- public var iheight(get,never) : Int;
- inline function get_iheight() return Math.ceil(height + y) - iy;
- function new(tex : h3d.mat.Texture, x : Float, y : Float, w : Float, h : Float, dx : Float=0, dy : Float=0) {
- this.innerTex = tex;
- this.x = x;
- this.y = y;
- this.width = w;
- this.height = h;
- this.dx = dx;
- this.dy = dy;
- if( tex != null ) setTexture(tex);
- }
- /**
- Returns an underlying Texture instance.
- **/
- public inline function getTexture():h3d.mat.Texture {
- return innerTex;
- }
- /**
- Checks if Tile or underlying Texture were disposed.
- **/
- public function isDisposed() {
- return innerTex == null || innerTex.isDisposed();
- }
- function setTexture(tex : h3d.mat.Texture) {
- this.innerTex = tex;
- if( tex != null ) {
- this.u = x / tex.width;
- this.v = y / tex.height;
- this.u2 = (x + width) / tex.width;
- this.v2 = (y + height) / tex.height;
- }
- }
- /**
- Changes this Tile underlying texture to one used in the specified Tile.
- If Tile was scaled, new uv will cover new width and height instead of the original unscaled one.
- @param t The Tile used as a source of the Texture instance.
- It's possible to switch texture by referring the Texture instance directly, by using access hacks:
- ```haxe
- @:privateAccess tile.setTexture(myTextureInstance);
- ```
- **/
- public inline function switchTexture( t : Tile ) {
- setTexture(t.innerTex);
- }
- /**
- Create a sub-region of this Tile with specified size and offset.
- @param x The offset on top of the current Tile offset along the X axis.
- @param y The offset on top of the current Tile offset along the Y axis.
- @param w The width of the new Tile region. Can exceed current tile size.
- @param h The height of the new Tile region. Can exceed the current tile size.
- @param dx An optional visual offset of the new Tile along the X axis.
- @param dy An optional visual offset of the new Tile along the Y axis.
- **/
- public function sub( x : Float, y : Float, w : Float, h : Float, dx = 0., dy = 0. ) : Tile {
- return new Tile(innerTex, this.x + x, this.y + y, w, h, dx, dy);
- }
- /**
- Returns a new Tile with shifting origin point (`dx` and `dy`) to the tile center.
- To modify this Tile origin point, use `Tile.setCenterRatio`.
- **/
- public function center():Tile {
- return sub(0, 0, width, height, -(width * .5), -(height * .5));
- }
- /**
- Sets `dx` / `dy` as origin point dictated by `px` / `py` with a default being center.
- **/
- public inline function setCenterRatio(?px:Float=0.5, ?py:Float=0.5) : Void {
- dx = -(px*width);
- dy = -(py*height);
- }
- /**
- Flips the Tile horizontally. Note that `dx` is flipped as well.
- **/
- public function flipX() : Void {
- var tmp = u; u = u2; u2 = tmp;
- dx = -dx - width;
- }
- /**
- Flips the Tile vertically. Note that `dy` is flipped as well.
- **/
- public function flipY() : Void {
- var tmp = v; v = v2; v2 = tmp;
- dy = -dy - height;
- }
- /**
- Set the Tile position in the texture to the specified coordinate.
- **/
- public function setPosition(x : Float, y : Float) : Void {
- this.x = x;
- this.y = y;
- var tex = innerTex;
- if( tex != null ) {
- u = x / tex.width;
- v = y / tex.height;
- u2 = (x + width) / tex.width;
- v2 = (y + height) / tex.height;
- }
- }
- /**
- Set the Tile size in the texture to the specified dimensions.
- **/
- public function setSize(w : Float, h : Float) : Void {
- this.width = w;
- this.height = h;
- var tex = innerTex;
- if( tex != null ) {
- u2 = (x + w) / tex.width;
- v2 = (y + h) / tex.height;
- }
- }
- /**
- Rescales the Tile to be of the set width and height, but without affecting the uv coordinates.
- Using this method allows to upscale/downscale Tiles, but creates a mismatch between the tile uv and width/height values.
- Due to that, using any methods that modify the uv value will cause the new uv to treat scaled width and height as true dimensions
- and can lead to unexpected results if not accounted for.
- **/
- public function scaleToSize( w : Float, h : Float ) : Void {
- this.width = w;
- this.height = h;
- }
- /**
- Scrolls the texture position by specified amount.
- **/
- public function scrollDiscrete( dx : Float, dy : Float ) : Void {
- var tex = innerTex;
- u += dx / tex.width;
- v -= dy / tex.height;
- u2 += dx / tex.width;
- v2 -= dy / tex.height;
- x = u * tex.width;
- y = v * tex.height;
- }
- /**
- Disposes of the Tile and its underlying Texture.
- Note that if Texture is used by other Tile instances, it will cause them to point at a disposed texture and can lead to errors.
- **/
- public function dispose() : Void {
- if( innerTex != null ) innerTex.dispose();
- innerTex = null;
- }
- /**
- Create a copy of this Tile instance.
- **/
- public function clone() : Tile {
- var t = new Tile(null, x, y, width, height, dx, dy);
- t.innerTex = innerTex;
- t.u = u;
- t.u2 = u2;
- t.v = v;
- t.v2 = v2;
- return t;
- }
- function get_xFlip() return u2 < u;
- function get_yFlip() return v2 < v;
- function set_xFlip(v) {
- if( v != xFlip ) flipX();
- return v;
- }
- function set_yFlip(v) {
- if( v != yFlip ) flipY();
- return v;
- }
- /**
- Split the Tile horizontally or vertically by the number of given frames.
- @param frames The amount of frames this Tile has to be split into.
- @param vertical Causes split to be done vertically instead of horizontal split.
- @param subpixel When enabled, retains the floating-point remainder if calculated frame size is not integral.
- **/
- public function split( frames : Int = 0, vertical = false, subpixel = false ) : Array<Tile> {
- var tl = [];
- if( vertical ) {
- if( frames == 0 )
- frames = Std.int(height / width);
- var stride = subpixel ? height / frames : Std.int(height / frames);
- for( i in 0...frames )
- tl.push(sub(0, i * stride, width, stride));
- } else {
- if( frames == 0 )
- frames = Std.int(width / height);
- var stride = subpixel ? width / frames : Std.int(width / frames);
- for( i in 0...frames )
- tl.push(sub(i * stride, 0, stride, height));
- }
- return tl;
- }
- /**
- Split the tile into a list of tiles of Size x Size pixels.
- @param size The width and height of the new Tiles.
- @param dx Optional visual offset of the new Tiles along the X axis.
- @param dy Optional visual offset of the new Tiles along the Y axis.
- @returns A one-dimensional array ordered in Y/X.
- **/
- public function gridFlatten( size : Float, dx = 0., dy = 0. ) : Array<Tile> {
- return [for( y in 0...Std.int(height / size) ) for( x in 0...Std.int(width / size) ) sub(x * size, y * size, size, size, dx, dy)];
- }
- /**
- Split the tile into a list of tiles of Size x Size pixels.
- @param size The width and height of the new Tiles.
- @param dx Optional visual offset of the new Tiles along the X axis.
- @param dy Optional visual offset of the new Tiles along the Y axis.
- @returns A two-dimensional array ordered in `[X][Y]`.
- **/
- public function grid( size : Float, dx = 0., dy = 0. ) : Array<Array<Tile>> {
- return [for( x in 0...Std.int(width / size) ) [for( y in 0...Std.int(height / size) ) sub(x * size, y * size, size, size, dx, dy)]];
- }
- @:dox(hide)
- public function toString() : String {
- return "Tile(" + x + "," + y + "," + width + "x" + height + (dx != 0 || dy != 0 ? "," + dx + ":" + dy:"") + ")";
- }
- function upload( bmp:hxd.BitmapData ) : Void {
- var w = innerTex.width;
- var h = innerTex.height;
- #if flash
- if( w != bmp.width || h != bmp.height ) {
- var bmp2 = new flash.display.BitmapData(w, h, true, 0);
- var p0 = new flash.geom.Point(0, 0);
- var bmp = bmp.toNative();
- bmp2.copyPixels(bmp, bmp.rect, p0, bmp, p0, true);
- innerTex.uploadBitmap(hxd.BitmapData.fromNative(bmp2));
- bmp2.dispose();
- } else
- #end
- innerTex.uploadBitmap(bmp);
- }
- /**
- Create a solid color Tile with specified width, height, color and alpha.
- @param color The RGB color of the Tile.
- @param width The width of the Tile in pixels.
- @param height The height of the Tile in pixels.
- @param alpha The transparency of the Tile.
- **/
- public static function fromColor( color : Int, ?width = 1, ?height = 1, ?alpha = 1. ) : Tile {
- var t = new Tile(h3d.mat.Texture.fromColor(color,alpha),0,0,1,1);
- // scale to size
- t.width = width;
- t.height = height;
- return t;
- }
- /**
- Creates a new Texture from provided BitmapData and returns a Tile representing it.
- **/
- public static function fromBitmap( bmp : hxd.BitmapData ) : Tile {
- var tex = h3d.mat.Texture.fromBitmap(bmp);
- return new Tile(tex, 0, 0, bmp.width, bmp.height);
- }
- /**
- Creates a new POT Texture from bmp and cuts it in a grid of tiles with maximum size of `[width, height]`.
- Algorithm will use bottom-right pixels as background color and cut out empty space from each Tile and
- will modify the origin point to retain the Tile position.
- Each row scan continues as long as there are no empty tiles.
- @param bmp The BitmapData which will be split into tiles.
- @param width The width of a single grid entry.
- @param height An optional height of a single grid entry. Width will be used if not provided.
- **/
- public static function autoCut( bmp : hxd.BitmapData, width : Int, ?height : Int ) : { main: Tile, tiles: Array<Array<Tile>> } {
- #if js
- bmp.lock();
- #end
- if( height == null ) height = width;
- var colorBG = bmp.getPixel(bmp.width - 1, bmp.height - 1);
- var tl = new Array();
- var w = 1, h = 1;
- while( w < bmp.width )
- w <<= 1;
- while( h < bmp.height )
- h <<= 1;
- var tex = new h3d.mat.Texture(w, h);
- for( y in 0...Std.int(bmp.height / height) ) {
- var a = [];
- tl[y] = a;
- for( x in 0...Std.int(bmp.width / width) ) {
- var sz = isEmpty(bmp, x * width, y * height, width, height, colorBG);
- if( sz == null )
- break;
- a.push(new Tile(tex,x*width+sz.dx, y*height+sz.dy, sz.w, sz.h, sz.dx, sz.dy));
- }
- }
- #if js
- bmp.unlock();
- #end
- var main = new Tile(tex, 0, 0, bmp.width, bmp.height);
- main.upload(bmp);
- return { main : main, tiles : tl };
- }
- /**
- Create new Tile from provided Texture instance.
- **/
- public static function fromTexture( t : h3d.mat.Texture ) : Tile {
- return new Tile(t, 0, 0, t.width, t.height);
- }
- /**
- Creates new POT Texture from Pixels and returns a Tile representing it.
- **/
- public static function fromPixels( pixels : hxd.Pixels ) : Tile {
- var pix2 = pixels.makeSquare(true);
- var t = h3d.mat.Texture.fromPixels(pix2);
- if( pix2 != pixels ) pix2.dispose();
- return new Tile(t, 0, 0, pixels.width, pixels.height);
- }
- static function isEmpty( b : hxd.BitmapData, px : Int, py : Int, width : Int, height : Int, bg : Int ) {
- var empty = true;
- var xmin = width, ymin = height, xmax = 0, ymax = 0;
- for( x in 0...width )
- for( y in 0...height ) {
- var color : Int = b.getPixel(x + px, y + py);
- if( color & 0xFF000000 == 0 ) {
- if( color != 0 ) b.setPixel(x + px, y + py, 0);
- continue;
- }
- if( color != bg ) {
- empty = false;
- if( x < xmin ) xmin = x;
- if( y < ymin ) ymin = y;
- if( x > xmax ) xmax = x;
- if( y > ymax ) ymax = y;
- }
- if( color == bg && color != 0 )
- b.setPixel(x + px, y + py, 0);
- }
- return empty ? null : { dx : xmin, dy : ymin, w : xmax - xmin + 1, h : ymax - ymin + 1 };
- }
- }
|