/** * 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 java.io.OutputStream; import loon.action.ActionControl; import loon.action.sprite.Sprites; import loon.canvas.Image; import loon.canvas.LColor; import loon.component.Desktop; import loon.font.IFont; import loon.opengl.GL20; import loon.opengl.GLEx; import loon.utils.ArrayByte; import loon.utils.ArrayByteOutput; import loon.utils.GLUtils; import loon.utils.GifEncoder; import loon.utils.MathUtils; import loon.utils.StringUtils; import loon.utils.processes.RealtimeProcessManager; import loon.utils.reply.Port; import loon.utils.timer.LTimer; import loon.utils.timer.LTimerContext; public class Display extends LSystemView { private GifEncoder gifEncoder; private boolean videoScreenToGif; private ArrayByteOutput videoCache; private final LTimer videoDelay = new LTimer(); /** * 返回video的缓存结果(不设置out对象时才会有效) * * @return */ public ArrayByte getVideoCache() { return videoCache.getArrayByte(); } /** * 开始录像(默认使用ArrayByte缓存录像结果到内存中) * * @return */ public GifEncoder startVideo() { return startVideo(videoCache = new ArrayByteOutput()); } /** * 开始录像(指定一个OutputStream对象,比如FileOutputStream 输出录像结果到指定硬盘位置) * * @param output * @return */ public GifEncoder startVideo(OutputStream output) { return startVideo(output, LSystem.isDesktop() ? LSystem.SECOND : LSystem.SECOND + LSystem.SECOND / 2); } /** * 开始录像(指定一个OutputStream对象,比如FileOutputStream 输出录像结果到指定硬盘位置) * * @param output * @param delay * @return */ public GifEncoder startVideo(OutputStream output, long delay) { stopVideo(); videoDelay.setDelay(delay); gifEncoder = new GifEncoder(); gifEncoder.start(output); gifEncoder.setDelay((int) delay); videoScreenToGif = true; return gifEncoder; } /** * 结束录像 * * @return */ public GifEncoder stopVideo() { if (gifEncoder != null) { gifEncoder.finish(); } videoScreenToGif = false; return gifEncoder; } private final RealtimeProcessManager manager; // 为了方便直接转码到C#和C++,无法使用匿名内部类(也就是在构造内直接构造实现的方式),只能都写出具体类来…… // PS:别提delegate,委托那玩意写出来太不优雅了(对于凭空实现某接口或抽象,而非局部重载来说),而且大多数J2C#的工具也不能直接转换过去…… private final class PaintPort extends Port<LTimerContext> { private final Display _display; PaintPort(Display d) { this._display = d; } @Override public void onEmit(LTimerContext clock) { _display.draw(clock); } } private final class UpdatePort extends Port<LTimerContext> { UpdatePort() { } @Override public void onEmit(LTimerContext clock) { manager.tick(clock); ActionControl.update(clock.timeSinceLastUpdate); } } private final class Logo implements LRelease { private int centerX = 0, centerY = 0; private float alpha = 0f; private float curFrame, curTime; boolean finish, inToOut; LTexture logo; public Logo(LTexture texture) { this.logo = texture; this.curTime = 60; this.curFrame = 0; this.inToOut = true; } public void draw(final GLEx gl) { if (logo == null || finish) { return; } if (!logo.isLoaded()) { this.logo.loadTexture(); } if (centerX == 0 || centerY == 0) { this.centerX = (int) (LSystem.viewSize.width) / 2 - logo.getWidth() / 2; this.centerY = (int) (LSystem.viewSize.height) / 2 - logo.getHeight() / 2; } if (logo == null || !logo.isLoaded()) { return; } alpha = (curFrame / curTime); if (inToOut) { curFrame++; if (curFrame == curTime) { alpha = 1f; inToOut = false; } } else if (!inToOut) { curFrame--; if (curFrame == 0) { alpha = 0f; finish = true; } } gl.setAlpha(alpha); gl.draw(logo, centerX, centerY); } @Override public void close() { if (logo != null) { logo.close(); logo = null; } } } private Runtime runtime; private long frameCount; private int frameRate, frames; private IFont fpsFont; private float cred, cgreen, cblue, calpha; private final GLEx glEx; private final LProcess process; private LSetting setting; boolean showLogo = false, initDrawConfig = false;; private Logo logoTex; private void newDefView(boolean show) { if (show && fpsFont == null) { this.fpsFont = LSystem.getSystemLogFont(); } showLogo = setting.isLogo; if (showLogo && !StringUtils.isEmpty(setting.logoPath)) { logoTex = new Logo(newTexture(setting.logoPath)); } } public Display(LGame game, int updateRate) { super(game, updateRate); setting = LSystem._base.setting; process = LSystem._process; manager = RealtimeProcessManager.get(); GL20 gl = game.graphics().gl; glEx = new GLEx(game.graphics(), game.graphics().defaultRenderTarget, gl); glEx.update(); paint.connect(new PaintPort(this)).setPriority(-1); update.connect(new UpdatePort()).setPriority(1); if (!setting.isLogo) { process.start(); } } public void setScreen(Screen screen) { process.setScreen(screen); } public LProcess getProcess() { return process; } /** * 清空当前游戏窗体内容为指定色彩 * * @param red * @param green * @param blue * @param alpha */ public void clearColor(float red, float green, float blue, float alpha) { cred = red; cgreen = green; cblue = blue; calpha = alpha; } /** * 清空当前游戏窗体内容为指定色彩 * * @param color */ public void clearColor(LColor color) { this.clearColor(color.r, color.g, color.b, color.a); } /** * 清空当前游戏窗体内容为纯黑色 */ public void clearColor() { this.clearColor(0, 0, 0, 0); } protected void draw(LTimerContext clock) { // fix渲染时机,避免调用渲染在纹理构造前 if (!initDrawConfig) { newDefView(setting.isFPS || setting.isLogo || setting.isMemory || setting.isSprites || setting.isDebug); initDrawConfig = true; } if (showLogo) { try { glEx.save(); glEx.begin(); glEx.clear(cred, cgreen, cblue, calpha); if (logoTex == null || logoTex.finish || logoTex.logo.disposed()) { showLogo = false; return; } logoTex.draw(glEx); if (logoTex.finish) { showLogo = false; logoTex.close(); logoTex = null; } } finally { glEx.end(); glEx.restore(); if (!showLogo) { process.start(); } } return; } if (!process.next()) { return; } try { glEx.saveTx(); glEx.begin(); glEx.reset(cred, cgreen, cblue, calpha); process.load(); process.calls(); process.runTimer(clock); process.draw(glEx); final boolean debug = setting.isDebug; // 显示fps速度 if (debug || setting.isFPS) { tickFrames(); fpsFont.drawString(glEx, "FPS:" + frameRate, 5, 5, 0, LColor.white); } // 显示内存 if (debug || setting.isMemory) { if (runtime == null) { runtime = Runtime.getRuntime(); } long totalMemory = runtime.totalMemory(); long currentMemory = totalMemory - runtime.freeMemory(); String memory = ((float) ((currentMemory * 10) >> 20) / 10) + " of " + ((float) ((runtime.maxMemory() * 10) >> 20) / 10) + " MB"; fpsFont.drawString(glEx, "MEMORY:" + memory, 5, 25, 0, LColor.white); } if (debug || setting.isSprites) { fpsFont.drawString(glEx, "SPRITE:" + Sprites.allSpritesCount() + "," + " DESKTOP:" + Desktop.allDesktopCount(), 5, 45, 0, LColor.white); } // 若打印日志到界面,很可能挡住游戏界面内容,所以isDisplayLog为true并且debug才显示 if (debug && setting.isDisplayLog) { process.paintLog(glEx, 5, 65); } process.drawEmulator(glEx); process.unload(); // 如果存在屏幕录像设置 if (videoScreenToGif && !LSystem.PAUSED && gifEncoder != null) { if (videoDelay.action(clock)) { Image tmp = GLUtils.getScreenshot(); Image image = null; if (LSystem.isDesktop()) { image = tmp; } else { // 因为内存和速度关系,考虑到全平台录制,因此默认只录屏幕大小的一半(否则在手机上绝对抗不了5分钟以上……) image = Image.getResize(tmp, (int) (process.getWidth() * 0.5f), (int) (process.getHeight() * 0.5f)); } gifEncoder.addFrame(image); if (tmp != null) { tmp.close(); tmp = null; } if (image != null) { image.close(); image = null; } } } } finally { glEx.end(); glEx.restoreTx(); process.resetTouch(); } } public Display resize(int viewWidth, int viewHeight) { process.resize(viewWidth, viewHeight); return this; } private void tickFrames() { long time = System.currentTimeMillis(); if (time - frameCount > 1000L) { frameRate = MathUtils.min(setting.fps, frames); frames = 0; frameCount = time; } frames++; } public int getFPS() { return frameRate; } public float getAlpha() { return calpha; } public float getRed() { return cred; } public float getGreen() { return cgreen; } public float getBlue() { return cblue; } public GLEx GL() { return glEx; } public float width() { return LSystem.viewSize.width(); } public float height() { return LSystem.viewSize.height; } public void close() { if (this.fpsFont != null) { this.fpsFont.close(); this.fpsFont = null; } if (this.logoTex != null) { this.logoTex.close(); this.logoTex = null; } initDrawConfig = false; } }