HaxePunk/HaxePunk

View on GitHub
haxepunk/graphics/Image.hx

Summary

Maintainability
Test Coverage
package haxepunk.graphics;

import haxepunk.Camera;
import haxepunk.Graphic;
import haxepunk.graphics.atlas.Atlas;
import haxepunk.graphics.atlas.IAtlasRegion;
import haxepunk.graphics.hardware.Texture;
import haxepunk.utils.Color;
import haxepunk.math.MathUtil;
import haxepunk.math.Rectangle;
import haxepunk.math.Vector2;

/**
 * Performance-optimized non-animated image. Can be drawn to the screen with transformations.
 */
class Image extends Graphic
{
    /**
     * Rotation of the image, in degrees.
     */
    public var angle:Float;

    /**
     * Scale of the image, effects both x and y scale.
     */
    public var scale:Float;

    /**
     * X scale of the image.
     */
    public var scaleX:Float;

    /**
     * Y scale of the image.
     */
    public var scaleY:Float;

    /**
     * Flips the image horizontally.
     */
    public var flipX:Bool = false;

    /**
     * Flips the image vertically.
     */
    public var flipY:Bool = false;

    public var flipped(get, set):Bool;
    inline function get_flipped() return flipX;
    inline function set_flipped(v:Bool) return flipX = v;

    /**
     * Constructor.
     * @param    source        Source image.
     * @param    clipRect    Optional rectangle defining area of the source image to draw.
     */
    public function new(?source:ImageType, ?clipRect:Rectangle)
    {
        super();
        init();

        _sourceRect = new Rectangle(0, 0, 1, 1);

        if (source != null)
        {
            _region = source;
            _sourceRect.width = _region.width;
            _sourceRect.height = _region.height;
        }

        if (clipRect != null)
        {
            if (clipRect.width == 0) clipRect.width = _sourceRect.width;
            if (clipRect.height == 0) clipRect.height = _sourceRect.height;
            _region = _region.clip(clipRect); // create a new clipped region
            _sourceRect = clipRect;
        }
    }

    /** @private Initialize variables */
    inline function init()
    {
        angle = 0;
        scale = scaleX = scaleY = 1;
        originX = originY = 0;

        alpha = 1;
        color = 0xFFFFFF;
    }

    @:dox(hide)
    override public function render(point:Vector2, camera:Camera)
    {
        var sx = scale * scaleX * (flipX ? -1 : 1),
            sy = scale * scaleY * (flipY ? -1 : 1),
            fsx = camera.screenScaleX,
            fsy = camera.screenScaleY;

        var x = floorX(camera, x),
            y = floorY(camera, y);
        if (flipX) x += floorX(camera, (originX * 2 - _region.width) * sx);
        if (flipY) y += floorY(camera, (originY * 2 - _region.height) * sy);

        if (angle == 0)
        {
            _point.x = floorX(camera, point.x) - floorX(camera, originX * sx) - floorX(camera, camera.x * scrollX) + x;
            _point.y = floorY(camera, point.y) - floorY(camera, originY * sy) - floorY(camera, camera.y * scrollY) + y;

            // render without rotation
            var clipRect = screenClipRect(camera, _point.x, _point.y);
            _region.draw(_point.x * fsx, _point.y * fsy,
                sx * fsx, sy * fsy, angle,
                color, alpha,
                shader, smooth, blend, clipRect, flexibleLayer
            );
        }
        else
        {
            _point.x = floorX(camera, point.x) - floorX(camera, originX) - floorX(camera, camera.x * scrollX) + x;
            _point.y = floorY(camera, point.y) - floorY(camera, originY) - floorY(camera, camera.y * scrollY) + y;
            var angle = angle * MathUtil.RAD;
            var cos = Math.cos(angle);
            var sin = Math.sin(angle);
            var a = sx * cos * fsx;
            var b = sx * sin * fsy;
            var c = -sy * sin * fsx;
            var d = sy * cos * fsy;
            var tx = (-originX * sx * cos + originY * sy * sin + originX + _point.x);
            var ty = (-originX * sx * sin - originY * sy * cos + originY + _point.y);
            var clipRect = screenClipRect(camera, tx, ty);
            _region.drawMatrix(tx * fsx, ty * fsy, a, b, c, d,
                color, alpha,
                shader, smooth, blend, clipRect, flexibleLayer
            );
        }
    }

    /**
     * Creates a new rectangle Image.
     * @param    width        Width of the rectangle.
     * @param    height        Height of the rectangle.
     * @param    color        Color of the rectangle.
     * @param    alpha        Alpha of the rectangle.
     * @return    A new Image object of a rectangle.
     */
    public static function createRect(width:Int, height:Int, color:Color = 0xFFFFFF, alpha:Float = 1):Image
    {
        if (width == 0 || height == 0)
            throw "Illegal rect, sizes cannot be 0.";

        var source:Texture = Texture.create(width, height, true, 0xFFFFFFFF);
        var image = new Image(Atlas.loadImageAsRegion(source));

        image.color = color;
        image.alpha = alpha;

        return image;
    }

    /**
     * Creates a new circle Image.
     * @param    radius        Radius of the circle.
     * @param    color        Color of the circle.
     * @param    alpha        Alpha of the circle.
     * @return    A new Image object of a circle.
     */
    public static function createCircle(radius:Int, color:Color = 0xFFFFFF, alpha:Float = 1):Image
    {
        if (radius == 0)
            throw "Illegal circle, radius cannot be 0.";

        var texture:Texture = Texture.create(radius * 2, radius * 2, true, 0);
        texture.drawCircle(radius, radius, radius);

        var image = new Image(Atlas.loadImageAsRegion(texture));

        image.color = color;
        image.alpha = alpha;

        return image;
    }

    /**
     * Centers the Image's originX/Y to its center.
     */
    override public function centerOrigin()
    {
        originX = Std.int(width / 2);
        originY = Std.int(height / 2);
    }

    /**
     * Centers the Image's originX/Y to its center, and negates the offset by the same amount.
     */
    public function centerOO()
    {
        x += originX;
        y += originY;
        centerOrigin();
        x -= originX;
        y -= originY;
    }

    /**
     * Width of the image.
     */
    public var width(get, never):Int;
    function get_width():Int return _region.width;

    /**
     * Height of the image.
     */
    public var height(get, never):Int;
    function get_height():Int return _region.height;

    /**
     * The scaled width of the image.
     */
    public var scaledWidth(get, set):Float;
    inline function get_scaledWidth():Float return width * scaleX * scale;
    inline function set_scaledWidth(w:Float):Float return scaleX = w / scale / width;

    /**
     * The scaled height of the image.
     */
    public var scaledHeight(get, set):Float;
    inline function get_scaledHeight():Float return height * scaleY * scale;
    inline function set_scaledHeight(h:Float):Float return scaleY = h / scale / height;

    override public function toString():String return '[$_class $width x $height]';

    // Source and buffer information.
    var _sourceRect:Rectangle;
    var _region:IAtlasRegion;
}