/**
* Copyright 2008 - 2015 The Loon Game Engine Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.5
*/
package loon;
import loon.canvas.Canvas;
import loon.font.TextFormat;
import loon.font.TextLayout;
import loon.font.TextWrap;
import loon.geom.Affine2f;
import loon.geom.Dimension;
import loon.geom.Matrix4;
import loon.opengl.GL20;
import loon.opengl.RenderTarget;
import loon.utils.Array;
import loon.utils.GLUtils;
import loon.utils.Scale;
import loon.utils.reply.UnitPort;
import static loon.opengl.GL20.*;
public abstract class Graphics {
protected final LGame game;
protected final Dimension viewSizeM = new Dimension();
protected Scale scale = null;
protected int viewPixelWidth, viewPixelHeight;
private Display display = null;
private Affine2f affine = null, lastAffine = null;
private Matrix4 viewMatrix = null;
private static Array<Matrix4> matrixsStack = new Array<Matrix4>();
// 创建一个半永久的纹理,用以批量进行颜色渲染
private static LTexture colorTex;
// 用以提供GL渲染服务
public final GL20 gl;
private class DefaultRender extends RenderTarget {
private final Graphics _graphics;
public DefaultRender(Graphics gfx) {
super(gfx);
_graphics = gfx;
}
public int id() {
return _graphics.defaultFramebuffer();
}
public int width() {
return _graphics.viewPixelWidth;
}
public int height() {
return _graphics.viewPixelHeight;
}
public float xscale() {
return _graphics.game.setting.scaling() ? LSystem.getScaleWidth()
: _graphics.scale.factor;
}
public float yscale() {
return _graphics.game.setting.scaling() ? LSystem.getScaleHeight()
: _graphics.scale.factor;
}
public boolean flip() {
return true;
}
}
public RenderTarget defaultRenderTarget = new DefaultRender(this);
/**
* 返回一个缩放比例,用以让当前设备加载的资源按照此比例进行资源缩放
*
* @return
*/
public Scale scale() {
return scale;
}
public Matrix4 getViewMatrix() {
display = game.display();
Dimension view = LSystem.viewSize;
if (viewMatrix == null) {
viewMatrix = new Matrix4();
viewMatrix.setToOrtho2D(0, 0, view.getWidth(), view.getHeight());
} else if (display != null && display.GL() != null
&& !(affine = display.GL().tx()).equals(lastAffine)) {
viewMatrix = affine.toViewMatrix4();
lastAffine = affine;
}
return viewMatrix;
}
public void save() {
if (viewMatrix != null) {
matrixsStack.add(viewMatrix = viewMatrix.cpy());
}
}
public void restore() {
viewMatrix = matrixsStack.pop();
}
public abstract Dimension screenSize();
public Canvas createCanvas(float width, float height) {
return createCanvasImpl(scale, scale.scaledCeil(width),
scale.scaledCeil(height));
}
public Canvas createCanvas(Dimension size) {
return createCanvas(size.width, size.height);
}
public LTexture createTexture(float width, float height,
LTexture.Format config) {
int texWidth = config.toTexWidth(scale.scaledCeil(width));
int texHeight = config.toTexHeight(scale.scaledCeil(height));
if (texWidth <= 0 || texHeight <= 0) {
throw new IllegalArgumentException("Invalid texture size: "
+ texWidth + "x" + texHeight);
}
int id = createTexture(config);
gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0,
GL_RGBA, GL_UNSIGNED_BYTE, null);
return new LTexture(this, id, config, texWidth,
texHeight, scale, width, height);
}
public LTexture createTexture(Dimension size, LTexture.Format config) {
return createTexture(size.width, size.height, config);
}
public abstract TextLayout layoutText(String text, TextFormat format);
public abstract TextLayout[] layoutText(String text, TextFormat format,
TextWrap wrap);
private class DisposePort extends UnitPort {
private final LRelease _release;
DisposePort(LRelease r) {
this._release = r;
}
@Override
public void onEmit() {
_release.close();
}
}
public void queueForDispose(final LRelease resource) {
game.frame.connect(new DisposePort(resource)).once();
}
public LTexture finalColorTex() {
if (colorTex == null) {
Canvas canvas = createCanvas(1, 1);
canvas.setFillColor(0xFFFFFFFF).fillRect(0, 0, canvas.width,
canvas.height);
colorTex = canvas.toTexture(LTexture.Format.NEAREST);
colorTex.setDisabledTexture(true);
}
return colorTex;
}
protected Graphics(LGame game, GL20 gl, Scale scale) {
this.game = game;
this.gl = gl;
this.scale = scale;
}
protected int defaultFramebuffer() {
return 0;
}
protected abstract Canvas createCanvasImpl(Scale scale, int pixelWidth,
int pixelHeight);
protected void viewportChanged(Scale scale, int viewWidth, int viewHeight) {
Display d = game.display();
if (!LSystem.LOCK_SCREEN) {
LSystem.viewSize.setSize(
(int) (viewWidth / LSystem.getScaleWidth()),
(int) (viewHeight / LSystem.getScaleHeight()));
if (viewMatrix != null) {
LSystem.viewSize.getMatrix().mul(viewMatrix);
}
this.scale = scale;
this.viewPixelWidth = viewWidth;
this.viewPixelHeight = viewHeight;
this.viewSizeM.width = game.setting.scaling() ? LSystem
.invXScaled(viewPixelWidth) : scale
.invScaled(viewPixelWidth);
this.viewSizeM.height = game.setting.scaling() ? LSystem
.invXScaled(viewPixelHeight) : scale
.invScaled(viewPixelHeight);
if (d != null) {
d.resize(viewPixelWidth, viewPixelHeight);
}
}
}
public int createTexture(LTexture.Format config) {
return createTexture(config, 0);
}
public int createTexture(LTexture.Format config, int count) {
int id = gl.glGenTexture() + count;
if (LTextures.contains(id)) {
return createTexture(config, 1);
}
if (GLUtils.getCurrentHardwareTextureID() == id) {
return createTexture(config, 1);
}
GLUtils.bindTexture(gl, id);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
config.magFilter);
int minFilter = mipmapify(config.minFilter, config.mipmaps);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
config.repeatX ? GL_REPEAT : GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
config.repeatY ? GL_REPEAT : GL_CLAMP_TO_EDGE);
return id;
}
protected static int mipmapify(int filter, boolean mipmaps) {
if (!mipmaps) {
return filter;
}
switch (filter) {
case GL_NEAREST:
return GL_NEAREST_MIPMAP_NEAREST;
case GL_LINEAR:
return GL_LINEAR_MIPMAP_NEAREST;
default:
return filter;
}
}
}