/*
* Copyright (c) 2003-onwards Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.shavenpuppy.jglib.resources;
import java.io.Serializable;
import org.lwjgl.util.Color;
import org.lwjgl.util.ReadableColor;
import org.lwjgl.util.ReadableRectangle;
import org.lwjgl.util.Rectangle;
import org.lwjgl.util.WritableDimension;
import org.w3c.dom.Element;
import com.shavenpuppy.jglib.opengl.ColorUtil;
import com.shavenpuppy.jglib.opengl.GLBaseTexture;
import com.shavenpuppy.jglib.opengl.GLRenderable;
import com.shavenpuppy.jglib.sprites.AlphaOp;
import com.shavenpuppy.jglib.sprites.SimpleRenderable;
import com.shavenpuppy.jglib.sprites.SimpleRenderer;
import com.shavenpuppy.jglib.sprites.SpriteImage;
import static org.lwjgl.opengl.GL11.*;
/**
* $Id: Background.java,v 1.32 2011/08/02 16:10:36 cix_foo Exp $
* <p>
* A Background is a texture split up into 9 areas. The corner areas
* are absolute in size; the edges and middle are tiled to fit the background.
* @author $Author: cix_foo $
* @version $Revision: 1.32 $
*/
public class Background extends Feature {
private static final long serialVersionUID = 1L;
private static final GLRenderable SETUP_BLEND = new GLRenderable() {
@Override
public void render() {
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
};
private static final GLRenderable SETUP_OPAQUE = new GLRenderable() {
@Override
public void render() {
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ZERO);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
};
public static enum Blend {
TRANSPARENT(SETUP_BLEND, AlphaOp.PREMULTIPLY),
GLOWING(SETUP_BLEND, AlphaOp.ZERO),
OPAQUE(SETUP_OPAQUE, AlphaOp.KEEP);
final GLRenderable op;
final AlphaOp alphaOp;
Blend(GLRenderable op, AlphaOp alphaOp) {
this.op = op;
this.alphaOp = alphaOp;
}
}
private static final Color TEMP = new Color();
/*
* Resource data
*/
/** Corners */
private String top_left, top_right, bottom_left, bottom_right;
/** Edges */
private String top, left, bottom, right;
/** Middle */
private String middle;
/** Color */
private MappedColor color, topColor, bottomColor, leftColor, rightColor, topLeftColor, bottomLeftColor, topRightColor, bottomRightColor;
/** Insets */
private Rectangle insets;
/** Default blending */
private Blend blend = Blend.TRANSPARENT;
/*
* Transient data
*/
private transient SpriteImage top_left_image, top_right_image, bottom_left_image,
bottom_right_image, top_image, left_image, bottom_image, right_image, middle_image;
/**
* Instances of the Background
*/
public class Instance implements SimpleRenderable, Serializable {
private static final long serialVersionUID = 1L;
/** Temp stuff */
private transient GLBaseTexture current;
private ReadableRectangle bounds;
private ReadableColor instanceColor = Color.WHITE;
private final Color blendColor = new Color();
private SimpleRenderer renderer;
private int alpha = 255;
private Blend blend = Background.this.blend;
private Instance() {
}
/**
* Sets the blend mode. Defaults to TRANSPARENT.
* @param blend
*/
public void setBlend(Blend blend) {
this.blend = blend;
}
@Override
public void render(SimpleRenderer renderer) {
renderer.glRender(blend.op);
renderBackground(renderer);
}
public void renderBackground(SimpleRenderer renderer) {
this.renderer = renderer;
current = null;
drawBottomLeft();
drawBottom();
drawLeft();
drawTopLeft();
drawBottomRight();
drawMiddle();
drawTop();
drawRight();
drawTopRight();
current = null;
renderer = null;
}
private int getW() {
return bounds.getWidth() + insets.getWidth() + insets.getX();
}
private int getH() {
return bounds.getHeight() + insets.getHeight() + insets.getY();
}
private void drawBottomLeft() {
int w = Math.min(getW(), bottom_left_image.getWidth());
int h = Math.min(getH(), bottom_left_image.getHeight());
drawImage(bottom_left_image, 0, 0, w, h);
}
private void drawBottom() {
int w = getW() - (bottom_left_image.getWidth() + bottom_right_image.getWidth());
int h = Math.min(bottom_image.getHeight(), getH());
drawImage(bottom_image, bottom_left_image.getWidth(), 0, w, h);
}
private void drawLeft() {
int w = Math.min(left_image.getWidth(), getW());
int h = getH() - (bottom_left_image.getHeight() + top_left_image.getHeight());
drawImage(left_image, 0, left_image.getY(), w, h);
}
private void drawTopLeft() {
int y = Math.max(bottom_left_image.getHeight(), getH() - top_left_image.getHeight());
int w = Math.min(getW(), top_left_image.getWidth());
int h = Math.min(getH() - y, top_left_image.getHeight());
drawImage(top_left_image, 0, y, w, h);
}
private void drawBottomRight() {
int x = Math.max(bottom_left_image.getWidth(), getW() - bottom_right_image.getWidth());
int w = Math.min(getW() - x, bottom_right_image.getWidth());
int h = Math.min(getH(), bottom_right_image.getHeight());
drawImage(bottom_right_image, x, 0, w, h);
}
private void drawMiddle() {
int w = getW() - (left_image.getWidth() + right_image.getWidth());
int h = getH() - (bottom_image.getHeight() + top_image.getHeight());
drawImage(middle_image, middle_image.getX(), middle_image.getY(), w, h);
}
private void drawTop() {
int y = Math.max(bottom_image.getHeight(), getH() - top_image.getHeight());
int w = getW() - (top_left_image.getWidth() + top_right_image.getWidth());
int h = Math.min(top_image.getHeight(), getH());
drawImage(top_image, top_left_image.getWidth(), y, w, h);
}
private void drawRight() {
int x = Math.max(left_image.getWidth(), getW() - right_image.getWidth());
int w = Math.min(right_image.getWidth(), getW());
int h = getH() - (bottom_right_image.getHeight() + top_right_image.getHeight());
drawImage(right_image, x, bottom_right_image.getHeight(), w, h);
}
private void drawTopRight() {
int x = Math.min(getW() - top_right_image.getWidth(), getW());
int y = Math.min(getH() - top_right_image.getHeight(), getH());
int w = getW() - x;
int h = getH() - y;
drawImage(top_right_image, x, y, w, h);
}
private void drawImage(SpriteImage image, int x, int y, int w, int h) {
if (w < 1 || h < 1) {
return;
}
blit(image, x, y, w, h);
}
private void calcColorAtPoint(int x, int y) {
float wid = getWidth();
float hgt = getHeight();
float fx = Math.min(wid, Math.max(0.0f, x - getRightInset()));
float fy = Math.min(hgt, Math.max(0.0f, y - getTopInset()));
double bottomLeftArea = fx * fy;
double bottomRightArea = (wid - fx) * fy;
double topLeftArea = fx * (hgt - fy);
double topRightArea = (wid - fx) * (hgt - fy);
double totalArea = wid * hgt;
double bottomLeftRatio = bottomLeftArea / totalArea;
double topLeftRatio = topLeftArea / totalArea;
double bottomRightRatio = bottomRightArea / totalArea;
double topRightRatio = topRightArea / totalArea;
blendColor.set
(
(int) (bottomLeftColor.getRed() * topRightRatio + topLeftColor.getRed() * bottomRightRatio + bottomRightColor.getRed() * topLeftRatio + topRightColor.getRed() * bottomLeftRatio),
(int) (bottomLeftColor.getGreen() * topRightRatio + topLeftColor.getGreen() * bottomRightRatio + bottomRightColor.getGreen() * topLeftRatio + topRightColor.getGreen() * bottomLeftRatio),
(int) (bottomLeftColor.getBlue() * topRightRatio + topLeftColor.getBlue() * bottomRightRatio + bottomRightColor.getBlue() * topLeftRatio + topRightColor.getBlue() * bottomLeftRatio),
(int) (bottomLeftColor.getAlpha() * topRightRatio + topLeftColor.getAlpha() * bottomRightRatio + bottomRightColor.getAlpha() * topLeftRatio + topRightColor.getAlpha() * bottomLeftRatio)
);
ColorUtil.blendColor(blendColor, instanceColor, TEMP);
blend.alphaOp.op(TEMP, alpha, renderer);
// float alpha00 = (blendColor.getAlpha() * instanceColor.getAlpha() * alpha) / 65025;
// float preMultAlpha = preMult ? alpha00 / 255.0f : 1.0f;
// float actualAlpha = preMult ? 0.0f : alpha00;
// renderer.glColor4ub
// (
// (byte) (preMultAlpha * blendColor.getRed() * instanceColor.getRed() / 255),
// (byte) (preMultAlpha * blendColor.getGreen() * instanceColor.getGreen() / 255),
// (byte) (preMultAlpha * blendColor.getBlue() * instanceColor.getBlue() / 255),
// (byte) (actualAlpha)
// );
}
private void blit(final SpriteImage image, int x, int y, int w, int h) {
if (current != image.getTexture()) {
current = image.getTexture();
renderer.glRender(current);
}
calcColorAtPoint(x, y);
renderer.glTexCoord2f(image.getTx0(), image.getTy0());
short idx = renderer.glVertex2f(x - insets.getX() + bounds.getX(), y - insets.getY() + bounds.getY());
float tx1 = image.getTx1();
calcColorAtPoint(x + w, y);
renderer.glTexCoord2f(tx1, image.getTy0());
renderer.glVertex2f(x + w - insets.getX() + bounds.getX(), y - insets.getY() + bounds.getY());
float ty1 = image.getTy1();
calcColorAtPoint(x + w, y + h);
renderer.glTexCoord2f(tx1, ty1);
renderer.glVertex2f(x + w - insets.getX() + bounds.getX(), y + h - insets.getY() + bounds.getY());
calcColorAtPoint(x, y + h);
renderer.glTexCoord2f(image.getTx0(), ty1);
renderer.glVertex2f(x - insets.getX() + bounds.getX(), y + h - insets.getY() + bounds.getY());
renderer.glRender(GL_TRIANGLE_FAN, new short[] {idx, (short) (idx + 1), (short) (idx + 2), (short) (idx + 3)});
}
public int getHeight() {
return bounds.getHeight() + insets.getHeight() + insets.getY();
}
public void getSize(WritableDimension dest) {
dest.setSize(bounds.getWidth() - insets.getWidth() - insets.getX(), bounds.getHeight() - insets.getHeight() - insets.getY());
}
public int getWidth() {
return bounds.getWidth() - insets.getWidth() - insets.getX();
}
public void setBounds(ReadableRectangle bounds) {
this.bounds = bounds;
}
public ReadableRectangle getBounds() {
return bounds;
}
public void setColor(ReadableColor src) {
instanceColor = src;
}
public void setAlpha(int alpha) {
this.alpha = alpha;
}
public ReadableColor getColor() {
return instanceColor;
}
public int getXInset() {
return insets.getX();
}
public int getYInset() {
return insets.getY();
}
public int getRightInset() {
return insets.getWidth();
}
public int getTopInset() {
return insets.getHeight();
}
}
/**
* C'tor
*/
public Background() {
super();
setAutoCreated();
}
/**
* @param name
*/
public Background(String name) {
super(name);
setAutoCreated();
}
/**
* Create an instance of the background
* @return a new Instance
*/
public Instance spawn() {
return new Instance();
}
@Override
public void load(Element element, Loader loader) throws Exception {
super.load(element, loader);
if (topColor == null) {
topColor = color;
}
if (bottomColor == null) {
bottomColor = color;
}
if (leftColor == null) {
leftColor = color;
}
if (rightColor == null) {
rightColor = color;
}
if (topLeftColor == null) {
topLeftColor = topColor;
}
if (topLeftColor == null) {
topLeftColor = leftColor;
}
if (topRightColor == null) {
topRightColor = topColor;
}
if (topRightColor == null) {
topRightColor = rightColor;
}
if (bottomLeftColor == null) {
bottomLeftColor = bottomColor;
}
if (bottomLeftColor == null) {
bottomLeftColor = leftColor;
}
if (bottomRightColor == null) {
bottomRightColor = bottomColor;
}
if (bottomRightColor == null) {
bottomRightColor = rightColor;
}
if (topLeftColor == null) {
topLeftColor = new MappedColor(ReadableColor.WHITE);
}
if (bottomLeftColor == null) {
bottomLeftColor = new MappedColor(ReadableColor.WHITE);
}
if (topRightColor == null) {
topRightColor = new MappedColor(ReadableColor.WHITE);
}
if (bottomRightColor == null) {
bottomRightColor = new MappedColor(ReadableColor.WHITE);
}
if (insets == null) {
insets = new Rectangle();
}
}
}