/* * This file is part of the Haven & Hearth game client. * Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and * Björn Johannessen <johannessen.bjorn@gmail.com> * * Redistribution and/or modification of this file is subject to the * terms of the GNU Lesser General Public License, version 3, as * published by the Free Software Foundation. * * This program 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. * * Other parts of this source tree adhere to other copying * rights. Please see the file `COPYING' in the root directory of the * source tree for details. * * A copy the GNU Lesser General Public License is distributed along * with the source tree of which this file is a part in the file * `doc/LPGL-3'. If it is missing for any reason, please see the Free * Software Foundation's website at <http://www.fsf.org/>, or write * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */ package haven; import java.awt.Color; import java.awt.image.*; import javax.media.opengl.*; import java.nio.*; public class GOut { public final GL2 gl; public final GLConfig gc; public Coord ul, sz, tx; private States.ColState color = new States.ColState(Color.WHITE); public final GLContext ctx; private final GOut root; public final GLState.Applier st; private final GLState.Buffer def2d; protected GOut(GOut o) { this.gl = o.gl; this.gc = o.gc; this.ul = o.ul; this.sz = o.sz; this.tx = o.tx; this.color = o.color; this.ctx = o.ctx; this.root = o.root; this.st = o.st; this.def2d = o.def2d; st.set(def2d); } public GOut(GL2 gl, GLContext ctx, GLConfig cfg, GLState.Applier st, GLState.Buffer def2d, Coord sz) { this.gl = gl; this.gc = cfg; this.ul = this.tx = Coord.z; this.sz = sz; this.ctx = ctx; this.st = st; this.root = this; this.def2d = def2d; } public static class GLException extends RuntimeException { public int code; public String str; private static javax.media.opengl.glu.GLU glu = new javax.media.opengl.glu.GLU(); public GLException(int code) { super("GL Error: " + code + " (" + glu.gluErrorString(code) + ")"); this.code = code; this.str = glu.gluErrorString(code); } } public static class GLInvalidEnumException extends GLException { public GLInvalidEnumException() {super(GL.GL_INVALID_ENUM);} } public static class GLInvalidValueException extends GLException { public GLInvalidValueException() {super(GL.GL_INVALID_VALUE);} } public static class GLInvalidOperationException extends GLException { public GLInvalidOperationException() {super(GL.GL_INVALID_OPERATION);} } public static class GLOutOfMemoryException extends GLException { public GLOutOfMemoryException() {super(GL.GL_OUT_OF_MEMORY);} } public static GLException glexcfor(int code) { switch(code) { case GL.GL_INVALID_ENUM: return(new GLInvalidEnumException()); case GL.GL_INVALID_VALUE: return(new GLInvalidValueException()); case GL.GL_INVALID_OPERATION: return(new GLInvalidOperationException()); case GL.GL_OUT_OF_MEMORY: return(new GLOutOfMemoryException()); default: return(new GLException(code)); } } public static void checkerr(GL gl) { int err = gl.glGetError(); if(err != 0) throw(glexcfor(err)); } private void checkerr() { checkerr(gl); } public GOut root() { return(root); } public GLState.Buffer basicstate() { return(def2d.copy()); } public void image(BufferedImage img, Coord c) { if(img == null) return; Tex tex = new TexI(img); image(tex, c); tex.dispose(); } public void image(Resource.Image img, Coord c) { if(img == null) return; image(img.tex(), c.add(img.o)); } /* Draw texture at c, quite simply. */ public void image(Tex tex, Coord c) { if(tex == null) return; st.set(def2d); state(color); tex.crender(this, c.add(tx), ul, sz); checkerr(); } public void image(Indir<Tex> tex, Coord c) { image(tex.get(), c); } public void aimage(Tex tex, Coord c, double ax, double ay) { Coord sz = tex.sz(); image(tex, c.add((int)((double)sz.x * -ax), (int)((double)sz.y * -ay))); } /* Draw texture at c, scaling it to sz. */ public void image(Tex tex, Coord c, Coord sz) { if(tex == null) return; st.set(def2d); state(color); tex.crender(this, c.add(tx), ul, this.sz, sz); checkerr(); } /* Draw texture at c, clipping everything outside ul to ul + sz. */ public void image(Tex tex, Coord c, Coord ul, Coord sz) { if(tex == null) return; st.set(def2d); state(color); ul = ul.add(this.tx); Coord br = ul.add(sz); if(ul.x < this.ul.x) ul.x = this.ul.x; if(ul.y < this.ul.y) ul.y = this.ul.y; if(br.x > this.ul.x + this.sz.x) br.x = this.ul.x + this.sz.x; if(br.y > this.ul.y + this.sz.y) br.y = this.ul.y + this.sz.y; tex.crender(this, c.add(this.tx), ul, br.sub(ul)); checkerr(); } /* Draw texture at c, with the extra state s applied. */ public void image(Tex tex, Coord c, GLState s) { st.set(def2d); if(s != null) state(s); tex.crender(this, c.add(tx), ul, sz); checkerr(); } public void vertex(Coord c) { gl.glVertex2i(c.x + tx.x, c.y + tx.y); } public void vertex(float x, float y) { gl.glVertex2f(x + tx.x, y + tx.y); } public void apply() { st.apply(this); } public void state(GLState st) { this.st.prep(st); } public void state2d() { st.set(def2d); } public void line(Coord c1, Coord c2, double w) { st.set(def2d); state(color); apply(); gl.glLineWidth((float)w); gl.glBegin(GL.GL_LINES); vertex(c1); vertex(c2); gl.glEnd(); checkerr(); } public void text(String text, Coord c) { atext(text, c, 0, 0); } public Coord atext(String text, Coord c, double ax, double ay) { Text t = Text.render(text); Tex T = t.tex(); Coord sz = t.sz(); image(T, c.add((int)((double)sz.x * -ax), (int)((double)sz.y * -ay))); T.dispose(); checkerr(); return sz; } public void poly(Coord... c) { st.set(def2d); state(color); apply(); gl.glBegin(GL2.GL_POLYGON); for(Coord vc : c) vertex(vc); gl.glEnd(); checkerr(); } public void poly2(Object... c) { st.set(def2d); st.put(States.color, States.vertexcolor); apply(); gl.glBegin(GL2.GL_POLYGON); for(int i = 0; i < c.length; i += 2) { Coord vc = (Coord)c[i]; Color col = (Color)c[i + 1]; gl.glColor4f((col.getRed() / 255.0f), (col.getGreen() / 255.0f), (col.getBlue() / 255.0f), (col.getAlpha() / 255.0f)); vertex(vc); } gl.glEnd(); checkerr(); } public void frect(Coord ul, Coord sz) { ul = tx.add(ul); Coord br = ul.add(sz); if(ul.x < this.ul.x) ul.x = this.ul.x; if(ul.y < this.ul.y) ul.y = this.ul.y; if(br.x > this.ul.x + this.sz.x) br.x = this.ul.x + this.sz.x; if(br.y > this.ul.y + this.sz.y) br.y = this.ul.y + this.sz.y; if((ul.x >= br.x) || (ul.y >= br.y)) return; st.set(def2d); state(color); apply(); gl.glBegin(GL2.GL_QUADS); gl.glVertex2i(ul.x, ul.y); gl.glVertex2i(br.x, ul.y); gl.glVertex2i(br.x, br.y); gl.glVertex2i(ul.x, br.y); gl.glEnd(); checkerr(); } public void frect(Coord c1, Coord c2, Coord c3, Coord c4) { st.set(def2d); state(color); apply(); gl.glBegin(GL2.GL_QUADS); vertex(c1); vertex(c2); vertex(c3); vertex(c4); gl.glEnd(); checkerr(); } public void ftexrect(Coord ul, Coord sz, GLState s, float tl, float tt, float tr, float tb) { ul = tx.add(ul); Coord br = ul.add(sz); Coord ult = new Coord(0, 0); Coord brt = new Coord(sz); if(ul.x < this.ul.x) { ult.x += this.ul.x - ul.x; ul.x = this.ul.x; } if(ul.y < this.ul.y) { ult.y += this.ul.y - ul.y; ul.y = this.ul.y; } if(br.x > this.ul.x + this.sz.x) { brt.x -= br.x - (this.ul.x + this.sz.x); br.x = this.ul.x + this.sz.x; } if(br.y > this.ul.y + this.sz.y) { brt.y -= br.y - (this.ul.y + this.sz.y); br.y = this.ul.y + this.sz.y; } if((ul.x >= br.x) || (ul.y >= br.y)) return; st.set(def2d); state(s); apply(); float l = tl + ((tr - tl) * ((float)ult.x) / ((float)sz.x)); float t = tt + ((tb - tt) * ((float)ult.y) / ((float)sz.y)); float r = tl + ((tr - tl) * ((float)brt.x) / ((float)sz.x)); float b = tt + ((tb - tt) * ((float)brt.y) / ((float)sz.y)); gl.glBegin(GL2.GL_QUADS); gl.glTexCoord2f(l, b); gl.glVertex2i(ul.x, ul.y); gl.glTexCoord2f(r, b); gl.glVertex2i(br.x, ul.y); gl.glTexCoord2f(r, t); gl.glVertex2i(br.x, br.y); gl.glTexCoord2f(l, t); gl.glVertex2i(ul.x, br.y); gl.glEnd(); checkerr(); } public void ftexrect(Coord ul, Coord sz, GLState s) { ftexrect(ul, sz, s, 0, 0, 1, 1); } public void fellipse(Coord c, Coord r, int a1, int a2) { st.set(def2d); state(color); apply(); gl.glBegin(GL.GL_TRIANGLE_FAN); vertex(c); for(int i = a1; i <= a2; i += 5) { double a = (i * Math.PI * 2) / 360.0; vertex(c.add((int)(Math.cos(a) * r.x), -(int)(Math.sin(a) * r.y))); } gl.glEnd(); checkerr(); } public void fellipse(Coord c, Coord r) { fellipse(c, r, 0, 360); } public void rect(Coord ul, Coord sz) { st.set(def2d); state(color); apply(); gl.glLineWidth(1); gl.glBegin(GL.GL_LINE_LOOP); vertex(ul.x + 0.5f, ul.y + 0.5f); vertex(ul.x + sz.x - 0.5f, ul.y + 0.5f); vertex(ul.x + sz.x - 0.5f, ul.y + sz.y - 0.5f); vertex(ul.x + 0.5f, ul.y + sz.y - 0.5f); gl.glEnd(); checkerr(); } public void prect(Coord c, Coord ul, Coord br, double a) { st.set(def2d); state(color); apply(); gl.glEnable(GL2.GL_POLYGON_SMOOTH); gl.glBegin(GL.GL_TRIANGLE_FAN); vertex(c); vertex(c.add(0, ul.y)); double p2 = Math.PI / 2; all: { float tc; tc = (float)(Math.tan(a) * -ul.y); if((a > p2) || (tc > br.x)) { vertex(c.x + br.x, c.y + ul.y); } else { vertex(c.x + tc, c.y + ul.y); break all; } tc = (float)(Math.tan(a - (Math.PI / 2)) * br.x); if((a > p2 * 2) || (tc > br.y)) { vertex(c.x + br.x, c.y + br.y); } else { vertex(c.x + br.x, c.y + tc); break all; } tc = (float)(-Math.tan(a - Math.PI) * br.y); if((a > p2 * 3) || (tc < ul.x)) { vertex(c.x + ul.x, c.y + br.y); } else { vertex(c.x + tc, c.y + br.y); break all; } tc = (float)(-Math.tan(a - (3 * Math.PI / 2)) * -ul.x); if((a > p2 * 4) || (tc < ul.y)) { vertex(c.x + ul.x, c.y + ul.y); } else { vertex(c.x + ul.x, c.y + tc); break all; } tc = (float)(Math.tan(a) * -ul.y); vertex(c.x + tc, c.y + ul.y); } gl.glEnd(); gl.glDisable(GL2.GL_POLYGON_SMOOTH); checkerr(); } public void chcolor(Color c) { if(c.equals(this.color.c)) return; this.color = new States.ColState(c); } public void chcolor(int r, int g, int b, int a) { chcolor(Utils.clipcol(r, g, b, a)); } public void chcolor() { chcolor(Color.WHITE); } Color getcolor() { return(color.c); } public GOut reclip(Coord ul, Coord sz) { GOut g = new GOut(this); g.tx = this.tx.add(ul); g.ul = new Coord(g.tx); Coord gbr = g.ul.add(sz), tbr = this.ul.add(this.sz); if(g.ul.x < this.ul.x) g.ul.x = this.ul.x; if(g.ul.y < this.ul.y) g.ul.y = this.ul.y; if(gbr.x > tbr.x) gbr.x = tbr.x; if(gbr.y > tbr.y) gbr.y = tbr.y; g.sz = gbr.sub(g.ul); return(g); } public GOut reclipl(Coord ul, Coord sz) { GOut g = new GOut(this); g.tx = this.tx.add(ul); g.ul = new Coord(g.tx); g.sz = sz; return(g); } public Color getpixel(Coord c) { byte[] buf = new byte[4]; gl.glReadPixels(c.x + tx.x, root.sz.y - c.y - tx.y, 1, 1, GL.GL_RGBA, GL2.GL_UNSIGNED_BYTE, ByteBuffer.wrap(buf)); checkerr(); return(new Color(((int)buf[0]) & 0xff, ((int)buf[1]) & 0xff, ((int)buf[2]) & 0xff)); } public BufferedImage getimage(Coord ul, Coord sz) { byte[] buf = new byte[sz.x * sz.y * 4]; gl.glReadPixels(ul.x + tx.x, root.sz.y - ul.y - sz.y - tx.y, sz.x, sz.y, GL.GL_RGBA, GL2.GL_UNSIGNED_BYTE, ByteBuffer.wrap(buf)); checkerr(); for(int y = 0; y < sz.y / 2; y++) { int to = y * sz.x * 4, bo = (sz.y - y - 1) * sz.x * 4; for(int o = 0; o < sz.x * 4; o++, to++, bo++) { byte t = buf[to]; buf[to] = buf[bo]; buf[bo] = t; } } WritableRaster raster = Raster.createInterleavedRaster(new DataBufferByte(buf, buf.length), sz.x, sz.y, 4 * sz.x, 4, new int[] {0, 1, 2, 3}, null); return(new BufferedImage(TexI.glcm, raster, false, null)); } public BufferedImage getimage() { return(getimage(Coord.z, sz)); } }