/* * This file is part of the Illarion project. * * Copyright © 2015 - Illarion e.V. * * Illarion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Illarion is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ package org.illarion.engine.backend.shared; import illarion.common.types.Rectangle; import illarion.common.util.FastMath; import org.illarion.engine.graphic.Sprite; import org.illarion.engine.graphic.Texture; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import java.util.Arrays; /** * This is the shared implementation of a sprite. It does only implement the required functions to hold the default * data of the sprites across the different backend implementations. * * @author Martin Karing <nitram@illarion.org> */ @Immutable public abstract class AbstractSprite<T extends Texture> implements Sprite { /** * The textures assigned to this sprite. */ @Nonnull private final T[] textures; /** * The X offset applied to the texture when rendering. */ private final int offsetX; /** * The Y offset applied to the sprite when rendering. */ private final int offsetY; /** * The X coordinate of the center coordinate of the sprite. */ private final float centerX; /** * The Y coordinate of the center coordinate of the sprite. */ private final float centerY; /** * This mirror flag. In case its set {@code true} the sprite will be rendered vertically mirrored. */ private final boolean mirror; /** * The rectangle that defines the area the sprite is displayed in. */ @Nonnull private final Rectangle displayRectangle; /** * Create a abstract sprite. * * @param textures the textures that are the frames of this sprite * @param offsetX the x offset of the sprite * @param offsetY the y offset of the sprite * @param centerX the offset of the center point long the x coordinate * @param centerY the offset of the center point long the y coordinate * @param mirror the mirrored flag */ protected AbstractSprite( @Nonnull T[] textures, int offsetX, int offsetY, float centerX, float centerY, boolean mirror) { if (textures.length == 0) { throw new IllegalArgumentException("Amount of textures does not fit."); } int width = textures[0].getWidth(); int height = textures[0].getHeight(); for (Texture texture : textures) { if ((texture.getWidth() != width) || (texture.getHeight() != height)) { throw new IllegalArgumentException("Sizes of textures do not match."); } } this.textures = Arrays.copyOf(textures, textures.length); this.offsetX = offsetX; this.offsetY = offsetY; this.centerX = centerX; this.centerY = centerY; this.mirror = mirror; double centerTransX = width * centerX; double centerTransY = height * centerY; long realOffsetX; if (mirror) { realOffsetX = Math.round(-centerTransX - offsetX); } else { realOffsetX = Math.round(-centerTransX + offsetX); } long realOffsetY = Math.round(-centerTransY - offsetY); displayRectangle = new Rectangle((int) realOffsetX, (int) realOffsetY, width, height); } @Override public int getWidth() { return textures[0].getWidth(); } @Override public int getHeight() { return textures[0].getHeight(); } @Override public int getFrames() { return textures.length; } @Override @Nonnull public T getFrame(int frame) { if ((frame < 0) || (frame >= textures.length)) { throw new IndexOutOfBoundsException("Frame out of bounds: " + frame); } return textures[frame]; } @Override public int getOffsetX() { return offsetX; } @Override public int getOffsetY() { return offsetY; } public float getCenterX() { return centerX; } public float getCenterY() { return centerY; } public boolean isMirrored() { return mirror; } @Nonnull @Override public Rectangle getDisplayArea( int x, int y, double scale, double rotation, @Nullable Rectangle storage) { @Nonnull Rectangle targetRectangle = (storage == null) ? new Rectangle() : storage; long displayWidth = FastMath.floor(displayRectangle.getWidth() * scale); long displayHeight = FastMath.floor(displayRectangle.getHeight() * scale); long displayX; if (isMirrored()) { displayX = FastMath.floor(x - (displayRectangle.getX() * scale) - displayWidth); } else { displayX = FastMath.floor(x + (displayRectangle.getX() * scale)); } long displayY = FastMath.floor(y + (displayRectangle.getY() * scale)); targetRectangle.set((int) displayX, (int) displayY, (int) displayWidth, (int) displayHeight); return targetRectangle; } }