From d7dbcc2c237536723c472192f075deab856a67e3 Mon Sep 17 00:00:00 2001 From: badlogicgames Date: Mon, 13 Dec 2010 16:38:02 +0000 Subject: [PATCH] [added] Angle backend. Experimental. Texture loading needs to be fixed, expects RGBA not ARGB. --- backends/gdx-backends-angle/.classpath | 14 + backends/gdx-backends-angle/.project | 17 + .../gdx/backends/angle/AngleApplication.java | 144 ++++ .../badlogic/gdx/backends/angle/AngleAudio.java | 207 ++++++ .../gdx/backends/angle/AngleAudioDevice.java | 91 +++ .../gdx/backends/angle/AngleAudioRecorder.java | 66 ++ .../gdx/backends/angle/AngleFileHandle.java | 57 ++ .../badlogic/gdx/backends/angle/AngleFiles.java | 48 ++ .../badlogic/gdx/backends/angle/AngleGLES0.java | 317 ++++++++ .../badlogic/gdx/backends/angle/AngleGraphics.java | 212 ++++++ .../badlogic/gdx/backends/angle/AngleInput.java | 272 +++++++ .../badlogic/gdx/backends/angle/AngleMusic.java | 171 +++++ .../badlogic/gdx/backends/angle/AnglePixmap.java | 176 +++++ .../badlogic/gdx/backends/angle/AngleSound.java | 160 ++++ .../badlogic/gdx/backends/angle/AngleTexture.java | 339 +++++++++ .../badlogic/gdx/backends/angle/PNGDecoder.java | 802 +++++++++++++++++++++ 16 files changed, 3093 insertions(+) create mode 100644 backends/gdx-backends-angle/.classpath create mode 100644 backends/gdx-backends-angle/.project create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleApplication.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudio.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudioDevice.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudioRecorder.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleFileHandle.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleFiles.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleGLES0.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleGraphics.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleInput.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleMusic.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AnglePixmap.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleSound.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleTexture.java create mode 100644 backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/PNGDecoder.java diff --git a/backends/gdx-backends-angle/.classpath b/backends/gdx-backends-angle/.classpath new file mode 100644 index 000000000..189a657e8 --- /dev/null +++ b/backends/gdx-backends-angle/.classpath @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/backends/gdx-backends-angle/.project b/backends/gdx-backends-angle/.project new file mode 100644 index 000000000..b89e95812 --- /dev/null +++ b/backends/gdx-backends-angle/.project @@ -0,0 +1,17 @@ + + + gdx-backend-angle + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleApplication.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleApplication.java new file mode 100644 index 000000000..ddb2be6ff --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleApplication.java @@ -0,0 +1,144 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ +package com.badlogic.gdx.backends.angle; + +import com.badlogic.anglejni.ESLoop; +import com.badlogic.anglejni.ESUtil; +import com.badlogic.gdx.Application; +import com.badlogic.gdx.ApplicationListener; +import com.badlogic.gdx.Audio; +import com.badlogic.gdx.Files; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Graphics; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.Version; + +public class AngleApplication implements Application, ESLoop { + AngleGraphics graphics; + AngleAudio audio; + AngleInput input; + AngleFiles files; + ESUtil utils; + ApplicationListener listener; + boolean created = false; + + public AngleApplication(final ApplicationListener listener,final String title,final int width,final int height,final boolean fullscreen) { + new Thread(new Runnable() { + public void run() { + Version.loadLibrary(); + + AngleApplication.this.listener = listener; + utils = new ESUtil(title, width, height, ESUtil.ES_WINDOW_DEPTH | (fullscreen?ESUtil.ES_WINDOW_FULLSCREEN:0)); + graphics = new AngleGraphics(width, height); + audio = new AngleAudio(); + input = new AngleInput(); + files = new AngleFiles(); + + Gdx.app = AngleApplication.this; + Gdx.graphics = graphics; + Gdx.audio = audio; + Gdx.input = input; + Gdx.files = files; + Gdx.gl = graphics.getGL20(); + Gdx.gl20 = graphics.getGL20(); + utils.run(AngleApplication.this); + } + }).run(); + } + + @Override + public Graphics getGraphics() { + return graphics; + } + + @Override + public Audio getAudio() { + return audio; + } + + @Override + public Input getInput() { + return input; + } + + @Override + public Files getFiles() { + return files; + } + + @Override + public void log(String tag, String message) { + System.out.println(tag + ": " + message); + } + + @Override + public ApplicationType getType() { + return ApplicationType.Desktop; + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public long getJavaHeap() { + return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + } + + @Override + public long getNativeHeap() { + return getJavaHeap(); + } + + + @Override + public void onKey(int action, int key, int keyCode) { + input.registerKeyEvent(action, key, keyCode); + } + + + @Override + public void onMouse(int action, int x, int y, int button) { + if(action != ES_MOUSE_WHEEL) + input.registerMouseEvent(action, x, y, button); + } + + + @Override + public void quit() { + listener.pause(); + listener.dispose(); + } + + + @Override + public void render() { + if(!created) { + listener.create(); + listener.resume(); + created = true; + } + input.processEvents(); + listener.render(); + } + + + @Override + public void resize(int width, int height) { + graphics.width = width; + graphics.height = height; + if(!created) + listener.resize(width, height); + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudio.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudio.java new file mode 100644 index 000000000..b01f0c2fb --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudio.java @@ -0,0 +1,207 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ +package com.badlogic.gdx.backends.angle; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.SourceDataLine; + +import com.badlogic.gdx.Audio; +import com.badlogic.gdx.audio.AudioDevice; +import com.badlogic.gdx.audio.AudioRecorder; +import com.badlogic.gdx.audio.Music; +import com.badlogic.gdx.audio.Sound; +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.GdxRuntimeException; + +/** + * An implementation of the {@link Audio} interface for the desktop. + * + * @author mzechner + * + */ +final class AngleAudio implements Audio, Runnable { + /** the audio line for sound effects **/ + private SourceDataLine line; + + /** The current buffers to play **/ + private final List buffers = new ArrayList(); + + /** The sound effects thread **/ + private Thread thread; + + private volatile boolean run = false; + + /** + * Helper class for playing back sound effects concurrently. + * + * @author mzechner + * + */ + class AngleSoundBuffer { + private final float[] samples; + private final AudioFormat format; + private final float volume; + private int writtenSamples = 0; + + public AngleSoundBuffer (AngleSound sound, float volume) throws Exception { + samples = sound.getAudioData(); + format = sound.getAudioFormat(); + this.volume = volume; + } + + /** + * Writes the next numFrames frames to the line for playback + * @return whether playback is done or not. + */ + public boolean writeSamples (int numSamples, float[] buffer) { + if (format.getChannels() == 1) { + int remainingSamples = Math.min(samples.length, writtenSamples + numSamples / 2); + for (int i = writtenSamples, j = 0; i < remainingSamples; i++, j += 2) { + buffer[j] += samples[i] * volume; + buffer[j + 1] += samples[i] * volume; + writtenSamples++; + } + } else { + int remainingSamples = Math.min(samples.length, writtenSamples + numSamples); + for (int i = writtenSamples, j = 0; i < remainingSamples; i += 2, j += 2) { + buffer[j] += samples[i] * volume; + buffer[j + 1] += samples[i + 1] * volume; + writtenSamples += 2; + } + } + + if (writtenSamples >= samples.length) + return false; + else + return true; + } + } + + AngleAudio () { + try { + AudioFormat format = new AudioFormat(44100.0f, 16, 2, true, false); + line = AudioSystem.getSourceDataLine(format); + line.open(format, 4410); + line.start(); + thread = new Thread(this, "LWJGL Audio"); + thread.setDaemon(true); + thread.start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public AudioDevice newAudioDevice (boolean isMono) { + return new AngleAudioDevice(isMono); + } + + public Music newMusic (FileHandle file) { + try { + AngleMusic music = new AngleMusic(((AngleFileHandle)file)); + return music; + } catch (Throwable e) { + throw new GdxRuntimeException("Couldn't create Music instance from file '" + file + "'", e); + } + } + + public Sound newSound (FileHandle file) { + try { + AngleSound sound = new AngleSound(this, ((AngleFileHandle)file)); + return sound; + } catch (Exception e) { + throw new GdxRuntimeException("Couldn't create Sound instance from file '" + file + "'", e); + } + } + + protected void enqueueSound (AngleSound sound, float volume) { + try { + synchronized (this) { + buffers.add(new AngleSoundBuffer(sound, volume)); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + public void run () { + + int NUM_SAMPLES = 44100 * 2; + float[] buffer = new float[NUM_SAMPLES]; + byte[] bytes = new byte[2 * NUM_SAMPLES]; + + run = true; + + while (run) { + int samplesToWrite = line.available() / 2; + + if (samplesToWrite > 0) { + fillBuffer(buffer, bytes, samplesToWrite); + int writtenBytes = line.write(bytes, 0, samplesToWrite * 2); + while (writtenBytes != samplesToWrite * 2) + writtenBytes += line.write(bytes, writtenBytes, samplesToWrite - writtenBytes); + } + + try { + Thread.sleep(5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + private void fillBuffer (float[] buffer, byte[] bytes, int samplesToWrite) { + for (int i = 0; i < buffer.length; i++) + buffer[i] = 0.0f; + for (int i = 0; i < bytes.length; i++) + bytes[i] = 0; + + int numBuffers = buffers.size(); + synchronized (this) { + Iterator bufferIter = buffers.iterator(); + while (bufferIter.hasNext()) { + AngleSoundBuffer soundBuffer = bufferIter.next(); + if (!soundBuffer.writeSamples(samplesToWrite, buffer)) bufferIter.remove(); + } + } + if (numBuffers > 0) { + for (int i = 0, j = 0; i < samplesToWrite; i++, j += 2) { + float fValue = buffer[i]; + if (fValue > 1) fValue = 1; + if (fValue < -1) fValue = -1; + short value = (short)(fValue * Short.MAX_VALUE); + bytes[j] = (byte)(value | 0xff); + bytes[j + 1] = (byte)(value >> 8); + } + } + } + + public AudioRecorder newAudioRecoder (int samplingRate, boolean isMono) { + return new AngleAudioRecorder(samplingRate, isMono); + } + + void dispose ( ) { + run = false; + try { + if(thread != null) + thread.join(); + if(line != null) + line.close(); + } catch (InterruptedException e) { + } + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudioDevice.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudioDevice.java new file mode 100644 index 000000000..d2fa54976 --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudioDevice.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ + +package com.badlogic.gdx.backends.angle; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.SourceDataLine; + +import com.badlogic.gdx.audio.AudioDevice; +import com.badlogic.gdx.utils.GdxRuntimeException; + +/** + * Implementation of the {@link AudioDevice} interface for the desktop using Java Sound. + * + * @author mzechner + * + */ +final class AngleAudioDevice implements AudioDevice { + /** the audio line **/ + private SourceDataLine line; + + /** whether this device is mono **/ + private final boolean isMono; + + /** byte buffer **/ + private byte[] bytes = new byte[44100 * 2 * 2]; + + public AngleAudioDevice (boolean isMono) { + this.isMono = isMono; + + try { + AudioFormat format = new AudioFormat(44100.0f, 16, isMono ? 1 : 2, true, false); + line = AudioSystem.getSourceDataLine(format); + line.open(format, 4410 * 2); + line.start(); + } catch (Exception e) { + throw new GdxRuntimeException("Couldn't createa AudioDevice", e); + } + } + + public void dispose () { + line.drain(); + line.close(); + } + + public boolean isMono () { + return isMono; + } + + public void writeSamples (short[] samples, int offset, int numSamples) { + if (bytes.length < samples.length * 2) bytes = new byte[samples.length * 2]; + + for (int i = offset, j = 0; i < offset + numSamples; i++, j += 2) { + short value = samples[i]; + bytes[j + 1] = (byte)(value & 0xff); + bytes[j] = (byte)(value >> 8); + } + + int writtenBytes = line.write(bytes, 0, numSamples * 2); + while (writtenBytes != numSamples * 2) + writtenBytes += line.write(bytes, writtenBytes, numSamples * 2 - writtenBytes); + } + + public void writeSamples (float[] samples, int offset, int numSamples) { + if (bytes.length < samples.length * 2) bytes = new byte[samples.length * 2]; + + for (int i = offset, j = 0; i < offset + numSamples; i++, j += 2) { + float fValue = samples[i]; + if (fValue > 1) fValue = 1; + if (fValue < -1) fValue = -1; + short value = (short)(fValue * Short.MAX_VALUE); + bytes[j] = (byte)(value | 0xff); + bytes[j + 1] = (byte)(value >> 8); + } + + int writtenBytes = line.write(bytes, 0, numSamples * 2); + while (writtenBytes != numSamples * 2) + writtenBytes += line.write(bytes, writtenBytes, numSamples * 2 - writtenBytes); + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudioRecorder.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudioRecorder.java new file mode 100644 index 000000000..c271647b0 --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleAudioRecorder.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ + +package com.badlogic.gdx.backends.angle; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioFormat.Encoding; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.TargetDataLine; + +import com.badlogic.gdx.audio.AudioRecorder; +import com.badlogic.gdx.utils.GdxRuntimeException; + +/** + * {@link AudioRecorder} implementation for the desktop using the java sound API. + * + * @author badlogicgames@gmail.com + * + */ +final class AngleAudioRecorder implements AudioRecorder { + /** the line we read the audio from **/ + private TargetDataLine line; + + /** the buffer to temporarily store the samples **/ + private byte[] buffer = new byte[1024 * 4]; + + public AngleAudioRecorder (int samplingRate, boolean isMono) { + try { + AudioFormat format = new AudioFormat(Encoding.PCM_SIGNED, samplingRate, 16, isMono ? 1 : 2, isMono ? 2 : 4, + samplingRate, false); + line = AudioSystem.getTargetDataLine(format); + line.open(format, buffer.length); + line.start(); + } catch (Exception ex) { + throw new GdxRuntimeException("Couldn't create AudioRecorder", ex); + } + } + + public void read (short[] samples, int offset, int numSamples) { + if (buffer.length < numSamples * 2) buffer = new byte[numSamples * 2]; + + int toRead = numSamples * 2; + int read = 0; + + while (read != toRead) + read += line.read(buffer, read, toRead - read); + + for (int i = 0, j = 0; i < numSamples * 2; i += 2, j++) { + samples[offset + j] = (short)((buffer[i] << 8) | (buffer[i + 1] & 0xff)); + } + } + + public void dispose () { + line.close(); + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleFileHandle.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleFileHandle.java new file mode 100644 index 000000000..50be3681d --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleFileHandle.java @@ -0,0 +1,57 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ + +package com.badlogic.gdx.backends.angle; + +import java.io.File; + +import com.badlogic.gdx.Files.FileType; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.files.FileHandle; + +/** + * @author mzechner + * @author Nathan Sweet + */ +final class AngleFileHandle extends FileHandle { + AngleFileHandle (String fileName, FileType type) { + super(fileName, type); + } + + AngleFileHandle (File file, FileType type) { + super(file, type); + } + + public FileHandle child (String name) { + return new AngleFileHandle(new File(file, name), type); + } + + public FileHandle parent () { + File parent = file.getParentFile(); + if (parent == null) { + switch (type) { + case Classpath: + case Absolute: + parent = new File("/"); + break; + case Internal: + parent = new File(""); + break; + case External: + parent = new File(Gdx.files.getExternalStoragePath()); + break; + } + } + return new AngleFileHandle(parent, type); + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleFiles.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleFiles.java new file mode 100644 index 000000000..0567a77f2 --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleFiles.java @@ -0,0 +1,48 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ +package com.badlogic.gdx.backends.angle; + +import com.badlogic.gdx.Files; +import com.badlogic.gdx.files.FileHandle; + +public class AngleFiles implements Files { + private final String externalPath = System.getProperty("user.home") + "/"; + + @Override public FileHandle getFileHandle (String fileName, FileType type) { + return new AngleFileHandle(fileName, type); + } + + @Override public FileHandle classpath (String path) { + return new AngleFileHandle(path, FileType.Classpath); + } + + @Override public FileHandle internal (String path) { + return new AngleFileHandle(path, FileType.Internal); + } + + @Override public FileHandle external (String path) { + return new AngleFileHandle(path, FileType.External); + } + + @Override public FileHandle absolute (String path) { + return new AngleFileHandle(path, FileType.Absolute); + } + + @Override public String getExternalStoragePath () { + return externalPath; + } + + @Override public boolean isExternalStorageAvailable () { + return true; + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleGLES0.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleGLES0.java new file mode 100644 index 000000000..e916adc16 --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleGLES0.java @@ -0,0 +1,317 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ +package com.badlogic.gdx.backends.angle; + +import java.nio.Buffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import com.badlogic.gdx.graphics.GL20; + +public class AngleGLES0 implements GL20 +{ + static + { + init( ); + } + + private static native void init( ); + + public native void glActiveTexture ( int texture ); + + public native void glAttachShader ( int program, int shader ); + + public native void glBindAttribLocation ( int program, int index, String name ); + + public native void glBindBuffer ( int target, int buffer ); + + public native void glBindFramebuffer ( int target, int framebuffer ); + + public native void glBindRenderbuffer ( int target, int renderbuffer ); + + public native void glBindTexture ( int target, int texture ); + + public native void glBlendColor ( float red, float green, float blue, float alpha ); + + public native void glBlendEquation ( int mode ); + + public native void glBlendEquationSeparate ( int modeRGB, int modeAlpha ); + + public native void glBlendFunc ( int sfactor, int dfactor ); + + public native void glBlendFuncSeparate ( int srcRGB, int dstRGB, int srcAlpha, int dstAlpha ); + + public native void glBufferData ( int target, int size, Buffer data, int usage ); + + public native void glBufferSubData ( int target, int offset, int size, Buffer data ); + + public native int glCheckFramebufferStatus ( int target ); + + public native void glClear ( int mask ); + + public native void glClearColor ( float red, float green, float blue, float alpha ); + + public native void glClearDepthf ( float depth ); + + public native void glClearStencil ( int s ); + + public native void glColorMask ( boolean red, boolean green, boolean blue, boolean alpha ); + + public native void glCompileShader ( int shader ); + + public native void glCompressedTexImage2D ( int target, int level, int internalformat, int width, int height, int border, int imageSize, Buffer data ); + + public native void glCompressedTexSubImage2D ( int target, int level, int xoffset, int yoffset, int width, int height, int format, int imageSize, Buffer data ); + + public native void glCopyTexImage2D ( int target, int level, int internalformat, int x, int y, int width, int height, int border ); + + public native void glCopyTexSubImage2D ( int target, int level, int xoffset, int yoffset, int x, int y, int width, int height ); + + public native int glCreateProgram ( ); + + public native int glCreateShader ( int type ); + + public native void glCullFace ( int mode ); + + public native void glDeleteBuffers ( int n, IntBuffer buffers ); + + public native void glDeleteFramebuffers ( int n, IntBuffer framebuffers ); + + public native void glDeleteProgram ( int program ); + + public native void glDeleteRenderbuffers ( int n, IntBuffer renderbuffers ); + + public native void glDeleteShader ( int shader ); + + public native void glDeleteTextures ( int n, IntBuffer textures ); + + public native void glDepthFunc ( int func ); + + public native void glDepthMask ( boolean flag ); + + public native void glDepthRangef ( float zNear, float zFar ); + + public native void glDetachShader ( int program, int shader ); + + public native void glDisable ( int cap ); + + public native void glDisableVertexAttribArray ( int index ); + + public native void glDrawArrays ( int mode, int first, int count ); + + public native void glDrawElements ( int mode, int count, int type, Buffer indices ); + + public native void glDrawElements ( int mode, int count, int type, int indices ); + + public native void glEnable ( int cap ); + + public native void glEnableVertexAttribArray ( int index ); + + public native void glFinish ( ); + + public native void glFlush ( ); + + public native void glFramebufferRenderbuffer ( int target, int attachment, int renderbuffertarget, int renderbuffer ); + + public native void glFramebufferTexture2D ( int target, int attachment, int textarget, int texture, int level ); + + public native void glFrontFace ( int mode ); + + public native void glGenBuffers ( int n, IntBuffer buffers ); + + public native void glGenerateMipmap ( int target ); + + public native void glGenFramebuffers ( int n, IntBuffer framebuffers ); + + public native void glGenRenderbuffers ( int n, IntBuffer renderbuffers ); + + public native void glGenTextures ( int n, IntBuffer textures ); + + public native String glGetActiveAttrib ( int program, int index, IntBuffer size, Buffer type ); + + public native String glGetActiveUniform ( int program, int index, IntBuffer size, Buffer type ); + + public native void glGetAttachedShaders ( int program, int maxcount, Buffer count, IntBuffer shaders ); + + public native int glGetAttribLocation ( int program, String name ); + + public native void glGetBooleanv ( int pname, Buffer params ); + + public native void glGetBufferParameteriv ( int target, int pname, IntBuffer params ); + + public native int glGetError ( ); + + public native void glGetFloatv ( int pname, FloatBuffer params ); + + public native void glGetFramebufferAttachmentParameteriv ( int target, int attachment, int pname, IntBuffer params ); + + public native void glGetIntegerv ( int pname, IntBuffer params ); + + public native void glGetProgramiv ( int program, int pname, IntBuffer params ); + + public native String glGetProgramInfoLog ( int program ); + + public native void glGetRenderbufferParameteriv ( int target, int pname, IntBuffer params ); + + public native void glGetShaderiv ( int shader, int pname, IntBuffer params ); + + public native String glGetShaderInfoLog ( int shader ); + + public native void glGetShaderPrecisionFormat ( int shadertype, int precisiontype, IntBuffer range, IntBuffer precision ); + + public native void glGetShaderSource ( int shader, int bufsize, Buffer length, String source ); + + public native String glGetString ( int name ); + + public native void glGetTexParameterfv ( int target, int pname, FloatBuffer params ); + + public native void glGetTexParameteriv ( int target, int pname, IntBuffer params ); + + public native void glGetUniformfv ( int program, int location, FloatBuffer params ); + + public native void glGetUniformiv ( int program, int location, IntBuffer params ); + + public native int glGetUniformLocation ( int program, String name ); + + public native void glGetVertexAttribfv ( int index, int pname, FloatBuffer params ); + + public native void glGetVertexAttribiv ( int index, int pname, IntBuffer params ); + + public native void glGetVertexAttribPointerv ( int index, int pname, Buffer pointer ); + + public native void glHint ( int target, int mode ); + + public native boolean glIsBuffer ( int buffer ); + + public native boolean glIsEnabled ( int cap ); + + public native boolean glIsFramebuffer ( int framebuffer ); + + public native boolean glIsProgram ( int program ); + + public native boolean glIsRenderbuffer ( int renderbuffer ); + + public native boolean glIsShader ( int shader ); + + public native boolean glIsTexture ( int texture ); + + public native void glLineWidth ( float width ); + + public native void glLinkProgram ( int program ); + + public native void glPixelStorei ( int pname, int param ); + + public native void glPolygonOffset ( float factor, float units ); + + public native void glReadPixels ( int x, int y, int width, int height, int format, int type, Buffer pixels ); + + public native void glReleaseShaderCompiler ( ); + + public native void glRenderbufferStorage ( int target, int internalformat, int width, int height ); + + public native void glSampleCoverage ( float value, boolean invert ); + + public native void glScissor ( int x, int y, int width, int height ); + + public native void glShaderBinary ( int n, IntBuffer shaders, int binaryformat, Buffer binary, int length ); + + public native void glShaderSource ( int shader, String string ); + + public native void glStencilFunc ( int func, int ref, int mask ); + + public native void glStencilFuncSeparate ( int face, int func, int ref, int mask ); + + public native void glStencilMask ( int mask ); + + public native void glStencilMaskSeparate ( int face, int mask ); + + public native void glStencilOp ( int fail, int zfail, int zpass ); + + public native void glStencilOpSeparate ( int face, int fail, int zfail, int zpass ); + + public native void glTexImage2D ( int target, int level, int internalformat, int width, int height, int border, int format, int type, Buffer pixels ); + + public native void glTexParameterf ( int target, int pname, float param ); + + public native void glTexParameterfv ( int target, int pname, FloatBuffer params ); + + public native void glTexParameteri ( int target, int pname, int param ); + + public native void glTexParameteriv ( int target, int pname, IntBuffer params ); + + public native void glTexSubImage2D ( int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, Buffer pixels ); + + public native void glUniform1f ( int location, float x ); + + public native void glUniform1fv ( int location, int count, FloatBuffer v ); + + public native void glUniform1i ( int location, int x ); + + public native void glUniform1iv ( int location, int count, IntBuffer v ); + + public native void glUniform2f ( int location, float x, float y ); + + public native void glUniform2fv ( int location, int count, FloatBuffer v ); + + public native void glUniform2i ( int location, int x, int y ); + + public native void glUniform2iv ( int location, int count, IntBuffer v ); + + public native void glUniform3f ( int location, float x, float y, float z ); + + public native void glUniform3fv ( int location, int count, FloatBuffer v ); + + public native void glUniform3i ( int location, int x, int y, int z ); + + public native void glUniform3iv ( int location, int count, IntBuffer v ); + + public native void glUniform4f ( int location, float x, float y, float z, float w ); + + public native void glUniform4fv ( int location, int count, FloatBuffer v ); + + public native void glUniform4i ( int location, int x, int y, int z, int w ); + + public native void glUniform4iv ( int location, int count, IntBuffer v ); + + public native void glUniformMatrix2fv ( int location, int count, boolean transpose, FloatBuffer value ); + + public native void glUniformMatrix3fv ( int location, int count, boolean transpose, FloatBuffer value ); + + public native void glUniformMatrix4fv ( int location, int count, boolean transpose, FloatBuffer value ); + + public native void glUseProgram ( int program ); + + public native void glValidateProgram ( int program ); + + public native void glVertexAttrib1f ( int indx, float x ); + + public native void glVertexAttrib1fv ( int indx, FloatBuffer values ); + + public native void glVertexAttrib2f ( int indx, float x, float y ); + + public native void glVertexAttrib2fv ( int indx, FloatBuffer values ); + + public native void glVertexAttrib3f ( int indx, float x, float y, float z ); + + public native void glVertexAttrib3fv ( int indx, FloatBuffer values ); + + public native void glVertexAttrib4f ( int indx, float x, float y, float z, float w ); + + public native void glVertexAttrib4fv ( int indx, FloatBuffer values ); + + public native void glVertexAttribPointer ( int indx, int size, int type, boolean normalized, int stride, Buffer ptr ); + + public native void glVertexAttribPointer ( int indx, int size, int type, boolean normalized, int stride, int ptr ); + + public native void glViewport ( int x, int y, int width, int height ); +} \ No newline at end of file diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleGraphics.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleGraphics.java new file mode 100644 index 000000000..d19011cd5 --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleGraphics.java @@ -0,0 +1,212 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ +package com.badlogic.gdx.backends.angle; + +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.io.InputStream; + +import javax.imageio.ImageIO; + +import com.badlogic.gdx.Graphics; +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.graphics.GL10; +import com.badlogic.gdx.graphics.GL11; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.GLCommon; +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.Pixmap.Format; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.Texture.TextureFilter; +import com.badlogic.gdx.graphics.Texture.TextureWrap; +import com.badlogic.gdx.graphics.TextureData; +import com.badlogic.gdx.utils.GdxRuntimeException; +import com.badlogic.gdx.utils.MathUtils; + +public class AngleGraphics implements Graphics { + GL20 gl; + int width; + int height; + long lastTime = System.nanoTime(); + long frameStart = System.nanoTime(); + int fps; + int frames; + float deltaTime = 0; + + AngleGraphics(int width, int height) { + gl = new AngleGLES20(); + } + + @Override + public boolean isGL11Available() { + return false; + } + + @Override + public boolean isGL20Available() { + return true; + } + + @Override + public GLCommon getGLCommon() { + return gl; + } + + @Override + public GL10 getGL10() { + return null; + } + + @Override + public GL11 getGL11() { + return null; + } + + @Override + public GL20 getGL20() { + return gl; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public float getDeltaTime() { + return deltaTime; + } + + @Override + public int getFramesPerSecond() { + return fps; + } + + @Override + public Pixmap newPixmap(int width, int height, Format format) { + return new AnglePixmap(width, height, format); + } + + @Override + public Pixmap newPixmap(InputStream in) { + try { + BufferedImage img = (BufferedImage) ImageIO.read(in); + return new AnglePixmap(img); + } catch (Exception ex) { + throw new GdxRuntimeException( + "Couldn't load Pixmap from InputStream", ex); + } + } + + @Override + public Pixmap newPixmap(FileHandle file) { + return newPixmap(file.read()); + } + + @Override + public Pixmap newPixmap(Object nativePixmap) { + return new AnglePixmap((BufferedImage) nativePixmap); + } + + @Override + public Texture newUnmanagedTexture(int width, int height, Format format, + TextureFilter minFilter, TextureFilter magFilter, + TextureWrap uWrap, TextureWrap vWrap) { + if (!MathUtils.isPowerOfTwo(width) || !MathUtils.isPowerOfTwo(height)) + throw new GdxRuntimeException( + "Texture dimensions must be a power of two: " + width + "x" + + height); + + if (format == Format.Alpha) + return new AngleTexture(width, height, + BufferedImage.TYPE_BYTE_GRAY, minFilter, magFilter, uWrap, + vWrap, false); + else + return new AngleTexture(width, height, + BufferedImage.TYPE_4BYTE_ABGR, minFilter, magFilter, uWrap, + vWrap, false); + } + + @Override + public Texture newUnmanagedTexture(Pixmap pixmap, TextureFilter minFilter, + TextureFilter magFilter, TextureWrap uWrap, TextureWrap vWrap) { + if (!MathUtils.isPowerOfTwo(pixmap.getHeight()) + || !MathUtils.isPowerOfTwo(pixmap.getWidth())) + throw new GdxRuntimeException( + "Texture dimensions must be a power of two: " + width + "x" + + height); + + return new AngleTexture((BufferedImage) pixmap.getNativePixmap(), + minFilter, magFilter, uWrap, vWrap, false); + } + + @Override + public Texture newTexture(FileHandle file, TextureFilter minFilter, + TextureFilter magFilter, TextureWrap uWrap, TextureWrap vWrap) { + Pixmap pixmap = newPixmap(file); + if (!MathUtils.isPowerOfTwo(pixmap.getHeight()) || !MathUtils.isPowerOfTwo(pixmap.getWidth())) + throw new GdxRuntimeException("Texture dimensions must be a power of two: " + file + " (" + pixmap.getWidth() + "x" + + pixmap.getHeight() + ")"); + + return new AngleTexture(file, minFilter, magFilter, uWrap, vWrap, false); + } + + @Override + public Texture newTexture(TextureData textureData, TextureFilter minFilter, + TextureFilter magFilter, TextureWrap uWrap, TextureWrap vWrap) { + return new AngleTexture(textureData, minFilter, magFilter, uWrap, vWrap); + } + + @Override + public GraphicsType getType() { + return GraphicsType.Angle; + } + + @Override + public float getPpiX() { + return Toolkit.getDefaultToolkit().getScreenResolution(); + } + + @Override + public float getPpiY() { + return Toolkit.getDefaultToolkit().getScreenResolution(); + } + + @Override + public float getPpcX() { + return (Toolkit.getDefaultToolkit().getScreenResolution() / 2.54f); + } + + @Override + public float getPpcY() { + return (Toolkit.getDefaultToolkit().getScreenResolution() / 2.54f); + } + + void updateTime () { + long time = System.nanoTime(); + deltaTime = (time - lastTime) / 1000000000.0f; + lastTime = time; + + if (time - frameStart >= 1000000000) { + fps = frames; + frames = 0; + frameStart = time; + } + frames++; + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleInput.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleInput.java new file mode 100644 index 000000000..0036681dd --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleInput.java @@ -0,0 +1,272 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ +package com.badlogic.gdx.backends.angle; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +import com.badlogic.anglejni.ESLoop; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.InputProcessor; +import com.badlogic.gdx.utils.Pool; +import com.badlogic.gdx.utils.Pool.PoolObjectFactory; + +public class AngleInput implements Input { + + class KeyEvent { + static final int KEY_DOWN = 0; + static final int KEY_UP = 1; + static final int KEY_TYPED = 2; + + int type; + int keyCode; + char keyChar; + } + + class TouchEvent { + static final int TOUCH_DOWN = 0; + static final int TOUCH_UP = 1; + static final int TOUCH_DRAGGED = 2; + + int type; + int x; + int y; + int pointer; + } + + Pool freeKeyEvents = new Pool( + new PoolObjectFactory() { + + @Override + public KeyEvent createObject() { + return new KeyEvent(); + } + }, 1000); + + Pool freeTouchEvents = new Pool( + new PoolObjectFactory() { + + @Override + public TouchEvent createObject() { + return new TouchEvent(); + } + }, 1000); + + List keyEvents = new ArrayList(); + List touchEvents = new ArrayList(); + boolean mousePressed = false; + int mouseX = 0; + int mouseY = 0; + HashSet pressedKeys = new HashSet(); + + private InputProcessor processor; + + public float getAccelerometerX () { + return 0; + } + + public float getAccelerometerY () { + return 0; + } + + public float getAccelerometerZ () { + return 0; + } + + public void getTextInput (final TextInputListener listener, final String title, final String text) { + SwingUtilities.invokeLater(new Runnable() { + public void run () { + String output = JOptionPane.showInputDialog(null, title, text); + listener.input(output); + } + }); + } + + public int getX () { + // FIXME + return 0; + } + + public int getY () { + // FIXME + return 0; + } + + public boolean isAccelerometerAvailable () { + return false; + } + + public boolean isKeyPressed (int key) { + // FIXME + return false; + } + + public boolean isTouched () { + // FIXME + return false; + } + + public int getX (int pointer) { + if (pointer > 0) + return 0; + else + return getX(); + } + + public int getY (int pointer) { + if (pointer > 0) + return 0; + else + return getY(); + } + + public boolean isTouched (int pointer) { + if (pointer > 0) + return false; + else + return isTouched(); + } + + public boolean supportsMultitouch () { + return false; + } + + @Override public void setOnscreenKeyboardVisible (boolean visible) { + + } + + @Override public boolean supportsOnscreenKeyboard () { + return false; + } + + @Override public void setCatchBackKey (boolean catchBack) { + + } + + void processEvents() { + synchronized(this) { + if(processor!=null) { + InputProcessor processor = this.processor; + int len = keyEvents.size(); + for(int i=0; i < len; i++) { + KeyEvent e = keyEvents.get(i); + switch(e.type) { + case KeyEvent.KEY_DOWN: + processor.keyDown(e.keyCode); + break; + case KeyEvent.KEY_UP: + processor.keyUp(e.keyCode); + break; + case KeyEvent.KEY_TYPED: + processor.keyTyped(e.keyChar); + } + freeKeyEvents.free(e); + } + + len = touchEvents.size(); + for(int i=0; i < len; i++) { + TouchEvent e = touchEvents.get(i); + switch(e.type) { + case TouchEvent.TOUCH_DOWN: + processor.touchDown(e.x, e.y, e.pointer); + break; + case TouchEvent.TOUCH_UP: + processor.touchUp(e.x, e.y, e.pointer); + break; + case TouchEvent.TOUCH_DRAGGED: + processor.touchDragged(e.x, e.y, e.pointer); + } + freeTouchEvents.free(e); + } + } else { + int len = touchEvents.size(); + for(int i=0; i < len; i++) { + freeTouchEvents.free(touchEvents.get(i)); + } + + len = keyEvents.size(); + for(int i=0; i < len; i++) { + freeKeyEvents.free(keyEvents.get(i)); + } + } + + keyEvents.clear(); + touchEvents.clear(); + } + } + + + boolean isButtonPressed () { + return mousePressed; + } + + @Override + public void setInputProcessor(InputProcessor processor) { + this.processor = processor; + } + + void registerKeyEvent(int action, int key, int uniCode) { + synchronized(this) { + KeyEvent event = freeKeyEvents.newObject(); + event.keyChar = (char)uniCode; + event.keyCode = translateKey(key); + + switch(action) { + case ESLoop.ES_KEY_DOWN: + event.type = KeyEvent.KEY_DOWN; + break; + case ESLoop.ES_KEY_UP: + event.type = KeyEvent.KEY_UP; + break; + case ESLoop.ES_KEY_TYPED: + event.type = KeyEvent.KEY_TYPED; + break; + } + + keyEvents.add(event); + } + } + + void registerMouseEvent(int action, int x, int y, int button) { + if(button != 1) + return; + + synchronized(this) { + TouchEvent event = freeTouchEvents.newObject(); + event.x = x; + event.y = y; + event.pointer = 0; + + switch(action) { + case ESLoop.ES_MOUSE_DOWN: + event.type = TouchEvent.TOUCH_DOWN; + break; + case ESLoop.ES_MOUSE_UP: + event.type = TouchEvent.TOUCH_UP; + break; + case ESLoop.ES_MOUSE_MOVE: + event.type = TouchEvent.TOUCH_DRAGGED; + break; + } + } + } + + int translateKey(int keyCode) { + // FIXME + return keyCode; + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleMusic.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleMusic.java new file mode 100644 index 000000000..4e51bcc98 --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleMusic.java @@ -0,0 +1,171 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ + +package com.badlogic.gdx.backends.angle; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.FloatControl; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.SourceDataLine; +import javax.sound.sampled.UnsupportedAudioFileException; + +import com.badlogic.gdx.audio.Music; +import com.badlogic.gdx.files.FileHandle; + +final class AngleMusic implements Music, Runnable { + private final int Playing = 0; + private final int Stopped = 1; + private final int Paused = 2; + + private AtomicInteger state = new AtomicInteger(Stopped); + private final Thread thread; + private final FileHandle handle; + private AudioInputStream ain; + private final SourceDataLine line; + private final byte[] buffer; + private AtomicBoolean looping = new AtomicBoolean(false); + private boolean disposed = false; + + public AngleMusic (FileHandle handle) throws UnsupportedAudioFileException, IOException, LineUnavailableException { + this.handle = handle; + + openAudioInputStream(); + AudioFormat audioFormat = ain.getFormat(); + line = AudioSystem.getSourceDataLine(audioFormat); + line.open(audioFormat); // FIXME reduce latency, gotta reimplement the playback thread. + line.start(); + buffer = new byte[10000 * ain.getFormat().getFrameSize()]; + ain.close(); + ain = null; + + thread = new Thread(this); + thread.setDaemon(true); + thread.start(); + } + + private void openAudioInputStream () throws UnsupportedAudioFileException, IOException { + ain = AudioSystem.getAudioInputStream(new BufferedInputStream(handle.read())); + AudioFormat baseFormat = ain.getFormat(); + AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16, + baseFormat.getChannels(), baseFormat.getChannels() * 2, baseFormat.getSampleRate(), false); + + ain = AudioSystem.getAudioInputStream(decodedFormat, ain); + } + + @Override public void dispose () { + disposed = true; + try { + thread.join(); + line.close(); + ain.close(); + } catch (Exception e) { + // nothing we can do here + } + } + + @Override public boolean isLooping () { + return looping.get(); + } + + @Override public boolean isPlaying () { + return state.get() == Playing; + } + + @Override public void pause () { + state.compareAndSet(Playing, Paused); + } + + @Override public void play () { + state.set(Playing); + } + + @Override public void stop () { + state.set(Stopped); + } + + @Override public void setLooping (boolean isLooping) { + looping.set(isLooping); + } + + @Override public void setVolume (float volume) { + try { + volume = Math.min(1, volume); + volume = Math.max(0, volume); + FloatControl control = (FloatControl)line.getControl(FloatControl.Type.MASTER_GAIN); + control.setValue(-80 + volume * 80); + } catch (IllegalArgumentException ex) { + ex.printStackTrace(); + } + } + + @Override public void run () { + int readBytes = 0; + long readSamples = 0; + + while (!disposed) { + int curState = state.get(); + if (curState == Playing) { + try { + if (ain == null) openAudioInputStream(); + readBytes = ain.read(buffer); + + if (readBytes != -1) { + int writtenBytes = line.write(buffer, 0, readBytes); + while (writtenBytes != readBytes) + writtenBytes += line.write(buffer, writtenBytes, readBytes - writtenBytes); + readSamples += readBytes / ain.getFormat().getFrameSize(); + } else { + System.out.println("samples: " + readSamples); + ain.close(); + if (!isLooping()) state.set(Stopped); + else openAudioInputStream(); + } + } catch (Exception ex) { + try { + ain.close(); + } catch (IOException e) { + e.printStackTrace(); + } + line.close(); + ex.printStackTrace(); + state.set(Stopped); + return; + } + } + else if (curState == Stopped && ain != null) + { + try { + ain.close(); + } catch (IOException e) { + e.printStackTrace(); + } + ain = null; + line.flush(); + } + + try { + Thread.sleep(10); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AnglePixmap.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AnglePixmap.java new file mode 100644 index 000000000..f4697cb01 --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AnglePixmap.java @@ -0,0 +1,176 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ + +package com.badlogic.gdx.backends.angle; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; + +import com.badlogic.gdx.graphics.Pixmap; + +/** + * An implementation of Pixmap based on the java graphics framework. + * + * @author badlogicgames@gmail.com + * + */ +final class AnglePixmap implements Pixmap { + BufferedImage pixmap; + Composite composite; + Color color = new Color(0); + int strokeWidth = 1; + + AnglePixmap (int width, int height, Pixmap.Format format) { + int internalformat = getInternalFormat(format); + pixmap = new BufferedImage(width, height, internalformat); + Graphics2D g = pixmap.createGraphics(); + g.setComposite(AlphaComposite.Clear); + g.setColor(new Color(0, 0, 0, 0)); + g.fillRect(0, 0, pixmap.getWidth(), pixmap.getHeight()); + g.dispose(); + composite = AlphaComposite.SrcOver; + } + + AnglePixmap (BufferedImage image) { + if (image == null) throw new IllegalArgumentException("image cannot be null."); + pixmap = convert(image); + } + + private BufferedImage convert (BufferedImage image) { + BufferedImage out = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE); + Graphics2D g = out.createGraphics(); + g.setComposite(AlphaComposite.Clear); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.setComposite(AlphaComposite.SrcOver); + g.drawImage(image, 0, 0, null); + g.dispose(); + return out; + } + + private int getInternalFormat (Pixmap.Format format) { + return BufferedImage.TYPE_4BYTE_ABGR; + } + + public void drawCircle (int x, int y, int radius) { + Graphics2D g = (Graphics2D)pixmap.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g.setComposite(composite); + g.setColor(color); + g.setStroke(new BasicStroke(strokeWidth)); + g.drawRect(x, y, radius * 2, radius * 2); + g.dispose(); + + } + + public void drawLine (int x, int y, int x2, int y2) { + Graphics2D g = (Graphics2D)pixmap.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setComposite(composite); + g.setColor(color); + g.setStroke(new BasicStroke(strokeWidth)); + g.drawLine(x, y, x2, y2); + g.dispose(); + + } + + public void drawRectangle (int x, int y, int width, int height) { + Graphics2D g = (Graphics2D)pixmap.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setComposite(composite); + g.setColor(color); + g.setStroke(new BasicStroke(strokeWidth)); + g.drawRect(x, y, width, height); + g.dispose(); + } + + public void fill () { + Graphics2D g = (Graphics2D)pixmap.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setComposite(composite); + g.setColor(color); + g.fillRect(0, 0, pixmap.getWidth(), pixmap.getHeight()); + g.dispose(); + } + + public void fillCircle (int x, int y, int radius) { + Graphics2D g = (Graphics2D)pixmap.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setComposite(composite); + g.setColor(color); + g.fillOval(x, y, radius * 2, radius * 2); + g.dispose(); + } + + public void fillRectangle (int x, int y, int width, int height) { + Graphics2D g = (Graphics2D)pixmap.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setComposite(composite); + g.setColor(color); + g.fillRect(x, y, width, height); + g.dispose(); + } + + public Object getNativePixmap () { + return pixmap; + } + + public void setColor (float r, float g, float b, float a) { + color = new Color(r, g, b, a); + } + + public void setStrokeWidth (int width) { + strokeWidth = width; + } + + public int getPixel (int x, int y) { + if (x < 0 || x >= pixmap.getWidth()) return 0; + if (y < 0 || y >= pixmap.getHeight()) return 0; + return pixmap.getRGB(x, y); + } + + public int getHeight () { + return pixmap.getHeight(); + } + + public int getWidth () { + return pixmap.getWidth(); + } + + public void drawPixmap (Pixmap pixmap, int x, int y, int srcx, int srcy, int width, int height) { + BufferedImage image = (BufferedImage)pixmap.getNativePixmap(); + + Graphics2D g = (Graphics2D)this.pixmap.getGraphics(); + g.setComposite(composite); + g.drawImage(image, x, y, x + width, y + height, srcx, srcy, srcx + width, srcy + height, null); + g.dispose(); + + } + + public void dispose () { + + } + + public void getPixelRow (int[] pixels, int y) { + for (int x = 0; x < pixmap.getWidth(); x++) { + pixels[x] = pixmap.getRGB(x, y); + } + + } + +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleSound.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleSound.java new file mode 100644 index 000000000..94a9c0cc1 --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleSound.java @@ -0,0 +1,160 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ + +package com.badlogic.gdx.backends.angle; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +import javax.sound.sampled.UnsupportedAudioFileException; + +import com.badlogic.gdx.audio.Sound; + +/** + * Implements the {@link Sound} interface for the desktop using {@link Clip}s internally. + * @author mzechner + * + */ +final class AngleSound implements Sound { + /** the audio format **/ + private final AudioFormat format; + + /** the audio data **/ + private final byte[] originalSamples; + + /** the float audio data **/ + private float[] samples; + + /** the audio instance **/ + private final AngleAudio audio; + + public AngleSound (AngleAudio audio, AngleFileHandle file) throws UnsupportedAudioFileException, IOException { + this.audio = audio; + InputStream fin = new BufferedInputStream(file.read()); + AudioInputStream ain = AudioSystem.getAudioInputStream(fin); + AudioFormat baseFormat = ain.getFormat(); + AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16, + baseFormat.getChannels(), baseFormat.getChannels() * 2, baseFormat.getSampleRate(), false); + + ain = AudioSystem.getAudioInputStream(decodedFormat, ain); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024 * 10]; + int readBytes = ain.read(buffer); + while (readBytes != -1) { + bytes.write(buffer, 0, readBytes); + readBytes = ain.read(buffer); + } + ain.close(); + System.out.println(decodedFormat); + format = decodedFormat; + originalSamples = bytes.toByteArray(); + + ByteBuffer tmpBuffer = ByteBuffer.wrap(originalSamples); + tmpBuffer.order(ByteOrder.LITTLE_ENDIAN); + ShortBuffer shorts = tmpBuffer.asShortBuffer(); + samples = new float[originalSamples.length / 2]; + for (int i = 0; i < samples.length; i++) { + float value = shorts.get(i) / (float)Short.MAX_VALUE; + if (value < -1) value = -1; + if (value > 1) value = 1; + samples[i] = value; + } + + samples = resample(samples, decodedFormat.getSampleRate(), decodedFormat.getChannels() == 1); + } + + private float[] resample (float[] samples, float sampleRate, boolean isMono) { + if (sampleRate == 44100) return samples; + + float idxInc = sampleRate / 44100; + int numSamples = (int)((samples.length / (float)sampleRate) * 44100); + if (!isMono && numSamples % 2 != 0) numSamples--; + + float[] newSamples = new float[numSamples]; + + if (isMono) { + float idx = 0; + for (int i = 0; i < newSamples.length; i++) { + int intIdx = (int)idx; + if (intIdx >= samples.length - 1) break; + + float value = samples[intIdx] + samples[intIdx + 1]; + value /= 2; + if (value > 1) value = 1; + if (value < -1) value = -1; + newSamples[i] = value; + idx += idxInc; + } + } else { + float idx = 0; + for (int i = 0; i < newSamples.length; i += 2) { + int intIdxL = (int)idx * 2; + int intIdxR = (int)idx * 2 + 1; + if (intIdxL >= samples.length - 2) break; + + float value = samples[intIdxL] + samples[intIdxL + 2]; + value /= 2; + if (value > 1) value = 1; + if (value < -1) value = -1; + newSamples[i] = value; + + value = samples[intIdxR] + samples[intIdxR + 2]; + value /= 2; + if (value > 1) value = 1; + if (value < -1) value = -1; + newSamples[i + 1] = value; + + idx += idxInc; + } + } + + return newSamples; + } + + public void dispose () { + + } + + public void play () { + audio.enqueueSound(this, 1); + } + + public void play (float volume) { + audio.enqueueSound(this, volume); + } + + /** + * @return the {@link AudioFormat} of the audio data + */ + public AudioFormat getAudioFormat () { + return format; + } + + /** + * @return the audio samples in form of a byte array + */ + public float[] getAudioData () { + return samples; + } + +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleTexture.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleTexture.java new file mode 100644 index 000000000..ce35dcf31 --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/AngleTexture.java @@ -0,0 +1,339 @@ +/* + * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) + * + * 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. + */ + +package com.badlogic.gdx.backends.angle; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBufferInt; +import java.awt.image.Raster; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.backends.angle.PNGDecoder.Format; +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.graphics.GL10; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.TextureData; +import com.badlogic.gdx.utils.BufferUtils; +import com.badlogic.gdx.utils.GdxRuntimeException; + +/** + * @author badlogicgames@gmail.com + * @author Nathan Sweet + */ +final class AngleTexture implements Texture { + /** height in pixels of texture **/ + private int texHeight; + /** width in pixels of texture **/ + private int texWidth; + /** whether this textures i managed or not **/ + private final boolean isManaged; + private int textureID; + private final boolean isMipMapped; + + /** global number of textures **/ + public static int textures = 0; + + static private ByteBuffer buffer; + static private IntBuffer intBuffer; + static private final PNGDecoder pngDecoder = new PNGDecoder(); + + AngleTexture (FileHandle file, TextureFilter minFilter, TextureFilter maxFilter, TextureWrap uWrap, TextureWrap vWrap, + boolean managed) { + this.isManaged = managed; + this.isMipMapped = TextureFilter.isMipMap(minFilter); + + if (!isMipMapped && file.path().endsWith(".png")) { + // Fast path. + loadPNG(file); + } else { + BufferedImage image = (BufferedImage)Gdx.graphics.newPixmap(file).getNativePixmap(); + loadMipMap(image); + } + bind(); + GL20 gl = Gdx.graphics.getGL20(); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, getTextureFilter(minFilter)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, getTextureFilter(maxFilter)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, getTextureWrap(uWrap)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, getTextureWrap(vWrap)); + textures++; + } + + AngleTexture (BufferedImage image, TextureFilter minFilter, TextureFilter maxFilter, TextureWrap uWrap, TextureWrap vWrap, + boolean managed) { + this.isManaged = managed; + this.isMipMapped = TextureFilter.isMipMap(minFilter); + + BufferedImage img = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR); + loadMipMap(img); + this.draw(Gdx.graphics.newPixmap(image), 0, 0); + bind(); + GL20 gl = Gdx.graphics.getGL20(); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, getTextureFilter(minFilter)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, getTextureFilter(maxFilter)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, getTextureWrap(uWrap)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, getTextureWrap(vWrap)); + textures++; + } + + AngleTexture (int width, int height, int format, TextureFilter minFilter, TextureFilter maxFilter, TextureWrap uWrap, + TextureWrap vWrap, boolean managed) { + this.isManaged = managed; + this.isMipMapped = TextureFilter.isMipMap(minFilter); + + BufferedImage image = new BufferedImage(width, height, format); + loadMipMap(image); + bind(); + GL20 gl = Gdx.graphics.getGL20(); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, getTextureFilter(minFilter)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, getTextureFilter(maxFilter)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, getTextureWrap(uWrap)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, getTextureWrap(vWrap)); + textures++; + } + + public AngleTexture (TextureData textureData, TextureFilter minFilter, TextureFilter magFilter, TextureWrap uWrap, + TextureWrap vWrap) { + isManaged = false; + this.isMipMapped = TextureFilter.isMipMap(minFilter); + + GL20 gl = Gdx.graphics.getGL20(); + IntBuffer buffer = BufferUtils.newIntBuffer(1); + gl.glGenTextures(1, buffer); + textureID = buffer.get(0); + bind(); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, getTextureFilter(minFilter)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, getTextureFilter(magFilter)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, getTextureWrap(uWrap)); + gl.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, getTextureWrap(vWrap)); + + textureData.load(); + texWidth = textureData.getWidth(); + texHeight = textureData.getHeight(); + + textures++; + } + + private void loadPNG (FileHandle file) { + try { + pngDecoder.decodeHeader(file.read()); + texWidth = pngDecoder.getWidth(); + texHeight = pngDecoder.getHeight(); + int stride = texWidth * 4; + ensureBufferSize(stride * texHeight); + + Format pngFormat = pngDecoder.decideTextureFormat(PNGDecoder.Format.RGBA); + int glFormat, glInternalFormat; + switch (pngFormat) { + case ALPHA: + glFormat = GL10.GL_ALPHA; + glInternalFormat = GL10.GL_ALPHA; + break; + case LUMINANCE: + glFormat = GL10.GL_LUMINANCE; + glInternalFormat = GL10.GL_LUMINANCE; + break; + case LUMINANCE_ALPHA: + glFormat = GL10.GL_LUMINANCE_ALPHA; + glInternalFormat = GL10.GL_LUMINANCE_ALPHA; + break; + case RGB: + glFormat = GL10.GL_RGB; + glInternalFormat = GL10.GL_RGB; + break; + case RGBA: + glFormat = GL10.GL_RGBA; + glInternalFormat = GL10.GL_RGBA; + break; +// case BGRA: +// glFormat = GL10.GL_BGRA; +// glInternalFormat = GL10.GL_BGRA; +// break; + default: + throw new UnsupportedOperationException("PNG format not handled: " + pngFormat); + } + pngDecoder.decode(buffer, stride, pngFormat); + buffer.flip(); + + GL20 gl = Gdx.graphics.getGL20(); + IntBuffer buffer = BufferUtils.newIntBuffer(1); + gl.glGenTextures(1, buffer); + textureID = buffer.get(0); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID); + gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, glInternalFormat, texWidth, texHeight, 0, glFormat, GL10.GL_UNSIGNED_BYTE, buffer); + } catch (IOException ex) { + throw new GdxRuntimeException("Error loading image file: " + file, ex); + } + } + + private ByteBuffer toByteBuffer (BufferedImage image) { + int width = image.getWidth(); + int height = image.getHeight(); + ensureBufferSize(width * height * 4); + + Raster raster = image.getRaster(); + if (image.getType() == BufferedImage.TYPE_INT_ARGB) + intBuffer.put(((DataBufferInt)raster.getDataBuffer()).getData(), 0, width * height); + else { + // Same as image.getRGB() without allocating a large int[]. + ColorModel colorModel = image.getColorModel(); + Object data = raster.getDataElements(0, 0, null); + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + intBuffer.put(colorModel.getRGB(raster.getDataElements(x, y, data))); + } + + buffer.limit(intBuffer.position() * 4); + return buffer; + } + + private void ensureBufferSize (int size) { + if (buffer == null || buffer.capacity() < size) { + buffer = BufferUtils.newByteBuffer(size); + ByteBuffer temp = buffer.slice(); + temp.order(ByteOrder.LITTLE_ENDIAN); + intBuffer = temp.asIntBuffer(); + } else { + buffer.clear(); + intBuffer.clear(); + } + } + + private BufferedImage scaleDown (BufferedImage image) { + BufferedImage scaled = new BufferedImage(image.getWidth() / 2, image.getHeight() / 2, BufferedImage.TYPE_4BYTE_ABGR_PRE); + Graphics2D g = scaled.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.drawImage(image, 0, 0, scaled.getWidth(), scaled.getHeight(), null); + g.dispose(); + return scaled; + } + + private void loadMipMap (BufferedImage image) { + int level = 0; + int height = image.getHeight(); + int width = image.getWidth(); + texWidth = width; + texHeight = height; + GL20 gl = Gdx.graphics.getGL20(); + IntBuffer buffer = BufferUtils.newIntBuffer(1); + gl.glGenTextures(1, buffer); + textureID = buffer.get(0); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID); + + while (height >= 1 || width >= 1 && level < 4) { + ByteBuffer imageBuffer = toByteBuffer(image); + gl.glTexImage2D(GL10.GL_TEXTURE_2D, level, GL10.GL_RGBA, width, height, 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, imageBuffer); + if (height == 1 || width == 1 || isMipMapped == false) break; + + level++; + if (height > 1) height /= 2; + if (width > 1) width /= 2; + + image = scaleDown(image); + } + } + + private int getTextureFilter (TextureFilter filter) { + if (filter == TextureFilter.Linear) + return GL10.GL_LINEAR; + else if (filter == TextureFilter.Nearest) + return GL10.GL_NEAREST; + else if (filter == TextureFilter.MipMap) + return GL10.GL_LINEAR_MIPMAP_LINEAR; + else if (filter == TextureFilter.MipMapNearestNearest) + return GL10.GL_NEAREST_MIPMAP_NEAREST; + else if (filter == TextureFilter.MipMapNearestLinear) + return GL10.GL_NEAREST_MIPMAP_LINEAR; + else if (filter == TextureFilter.MipMapLinearNearest) + return GL10.GL_LINEAR_MIPMAP_NEAREST; + else if (filter == TextureFilter.MipMapLinearLinear) + return GL10.GL_LINEAR_MIPMAP_LINEAR; + else + return GL10.GL_LINEAR_MIPMAP_LINEAR; + } + + private int getTextureWrap (TextureWrap wrap) { + if (wrap == TextureWrap.ClampToEdge) + return GL10.GL_CLAMP_TO_EDGE; + else + return GL10.GL_REPEAT; + } + + public boolean isManaged () { + return isManaged; + } + + public void bind () { + GL20 gl = Gdx.graphics.getGL20(); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID); + } + + public void dispose () { + GL20 gl = Gdx.graphics.getGL20(); + IntBuffer buffer = BufferUtils.newIntBuffer(1); + buffer.put(textureID); + buffer.flip(); + gl.glDeleteTextures(1, buffer); + textures--; + } + + public int getHeight () { + return texHeight; + } + + public int getWidth () { + return texWidth; + } + + public void draw (Pixmap pixmap, int x, int y) { + if (isManaged) throw new GdxRuntimeException("Can't draw to a managed texture"); + BufferedImage image = (BufferedImage)pixmap.getNativePixmap(); + + int level = 0; + int height = image.getHeight(); + int width = image.getWidth(); + GL20 gl = Gdx.graphics.getGL20(); + bind(); + while (height >= 1 || width >= 1 && level < 4) { + ByteBuffer imageBuffer = toByteBuffer(image); + gl.glTexSubImage2D(GL10.GL_TEXTURE_2D, level, x, y, width, height, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, imageBuffer); + if (height == 1 || width == 1 || isMipMapped == false) break; + + level++; + if (height > 1) height /= 2; + if (width > 1) width /= 2; + + image = scaleDown(image); + } + + } + + public int getTextureObjectHandle () { + return textureID; + } + + public void setWrap (TextureWrap x, TextureWrap y) { + bind(); + GL20 gl = Gdx.graphics.getGL20(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, x == TextureWrap.Repeat ? GL10.GL_REPEAT : GL10.GL_CLAMP_TO_EDGE); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, y == TextureWrap.Repeat ? GL10.GL_REPEAT : GL10.GL_CLAMP_TO_EDGE); + } +} diff --git a/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/PNGDecoder.java b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/PNGDecoder.java new file mode 100644 index 000000000..b9ee7407c --- /dev/null +++ b/backends/gdx-backends-angle/src/com/badlogic/gdx/backends/angle/PNGDecoder.java @@ -0,0 +1,802 @@ +/* + * Copyright (c) 2008-2010, Matthias Mann + * + * 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 Matthias Mann 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.badlogic.gdx.backends.angle; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.zip.CRC32; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +/** + * Decodes A PNG directly a ByteBuffer. + * + * @author Matthias Mann + * @author Nathan Sweet (minor edits to avoid allocation) + */ +final class PNGDecoder { + public enum Format { + ALPHA(1, true), LUMINANCE(1, false), LUMINANCE_ALPHA(2, true), RGB(3, false), RGBA(4, true), BGRA(4, true), ABGR(4, true); + + final int numComponents; + final boolean hasAlpha; + + private Format (int numComponents, boolean hasAlpha) { + this.numComponents = numComponents; + this.hasAlpha = hasAlpha; + } + + public int getNumComponents () { + return numComponents; + } + + public boolean isHasAlpha () { + return hasAlpha; + } + } + + private static final byte[] SIGNATURE = {(byte)137, 80, 78, 71, 13, 10, 26, 10}; + + private static final int IHDR = 0x49484452; + private static final int PLTE = 0x504C5445; + private static final int tRNS = 0x74524E53; + private static final int IDAT = 0x49444154; + private static final int IEND = 0x49454E44; + + private static final byte COLOR_GREYSCALE = 0; + private static final byte COLOR_TRUECOLOR = 2; + private static final byte COLOR_INDEXED = 3; + private static final byte COLOR_GREYALPHA = 4; + private static final byte COLOR_TRUEALPHA = 6; + + private InputStream input; + private final CRC32 crc; + private final byte[] buffer; + + private int chunkLength; + private int chunkType; + private int chunkRemaining; + + private int width; + private int height; + private int bitdepth; + private int colorType; + private int bytesPerPixel; + private byte[] palette; + private byte[] paletteA; + private byte[] transPixel; + + public PNGDecoder () { + this.crc = new CRC32(); + this.buffer = new byte[4096]; + } + + public void decodeHeader (InputStream input) throws IOException { + this.input = input; + + readFully(buffer, 0, SIGNATURE.length); + if (!checkSignatur(buffer)) { + throw new IOException("Not a valid PNG file"); + } + + openChunk(IHDR); + readIHDR(); + closeChunk(); + + searchIDAT: + for (;;) { + openChunk(); + switch (chunkType) { + case IDAT: + break searchIDAT; + case PLTE: + readPLTE(); + break; + case tRNS: + readtRNS(); + break; + } + closeChunk(); + } + + if (colorType == COLOR_INDEXED && palette == null) { + throw new IOException("Missing PLTE chunk"); + } + } + + public int getHeight () { + return height; + } + + public int getWidth () { + return width; + } + + public boolean hasAlpha () { + return colorType == COLOR_TRUEALPHA || paletteA != null || transPixel != null; + } + + public boolean isRGB () { + return colorType == COLOR_TRUEALPHA || colorType == COLOR_TRUECOLOR || colorType == COLOR_INDEXED; + } + + /** + * Computes the implemented format conversion for the desired format. + * + * @param fmt the desired format + * @return format which best matches the desired format + * @throws UnsupportedOperationException if this PNG file can't be decoded + */ + public Format decideTextureFormat (Format fmt) { + switch (colorType) { + case COLOR_TRUECOLOR: + switch (fmt) { + case ABGR: + case RGBA: + case BGRA: + case RGB: + return fmt; + default: + return Format.RGB; + } + case COLOR_TRUEALPHA: + switch (fmt) { + case ABGR: + case RGBA: + case BGRA: + case RGB: + return fmt; + default: + return Format.RGBA; + } + case COLOR_GREYSCALE: + switch (fmt) { + case LUMINANCE: + case ALPHA: + return fmt; + default: + return Format.LUMINANCE; + } + case COLOR_GREYALPHA: + return Format.LUMINANCE_ALPHA; + case COLOR_INDEXED: + switch (fmt) { + case ABGR: + case RGBA: + case BGRA: + return fmt; + default: + return Format.RGBA; + } + default: + throw new UnsupportedOperationException("Not yet implemented"); + } + } + + public void decode (ByteBuffer buffer, int stride, Format fmt) throws IOException { + final int offset = buffer.position(); + final int lineSize = ((width * bitdepth + 7) / 8) * bytesPerPixel; + byte[] curLine = new byte[lineSize + 1]; + byte[] prevLine = new byte[lineSize + 1]; + byte[] palLine = (bitdepth < 8) ? new byte[width + 1] : null; + + final Inflater inflater = new Inflater(); + try { + for (int y = 0; y < height; y++) { + readChunkUnzip(inflater, curLine, 0, curLine.length); + unfilter(curLine, prevLine); + + buffer.position(offset + y * stride); + + switch (colorType) { + case COLOR_TRUECOLOR: + switch (fmt) { + case ABGR: + copyRGBtoABGR(buffer, curLine); + break; + case RGBA: + copyRGBtoRGBA(buffer, curLine); + break; + case BGRA: + copyRGBtoBGRA(buffer, curLine); + break; + case RGB: + copy(buffer, curLine); + break; + default: + throw new UnsupportedOperationException("Unsupported format for this image"); + } + break; + case COLOR_TRUEALPHA: + switch (fmt) { + case ABGR: + copyRGBAtoABGR(buffer, curLine); + break; + case RGBA: + copy(buffer, curLine); + break; + case BGRA: + copyRGBAtoBGRA(buffer, curLine); + break; + case RGB: + copyRGBAtoRGB(buffer, curLine); + break; + default: + throw new UnsupportedOperationException("Unsupported format for this image"); + } + break; + case COLOR_GREYSCALE: + switch (fmt) { + case LUMINANCE: + case ALPHA: + copy(buffer, curLine); + break; + default: + throw new UnsupportedOperationException("Unsupported format for this image"); + } + break; + case COLOR_GREYALPHA: + switch (fmt) { + case LUMINANCE_ALPHA: + copy(buffer, curLine); + break; + default: + throw new UnsupportedOperationException("Unsupported format for this image"); + } + break; + case COLOR_INDEXED: + switch (bitdepth) { + case 8: + palLine = curLine; + break; + case 4: + expand4(curLine, palLine); + break; + case 2: + expand2(curLine, palLine); + break; + case 1: + expand1(curLine, palLine); + break; + default: + throw new UnsupportedOperationException("Unsupported bitdepth for this image"); + } + switch (fmt) { + case ABGR: + copyPALtoABGR(buffer, palLine); + break; + case RGBA: + copyPALtoRGBA(buffer, palLine); + break; + case BGRA: + copyPALtoBGRA(buffer, palLine); + break; + default: + throw new UnsupportedOperationException("Unsupported format for this image"); + } + break; + default: + throw new UnsupportedOperationException("Not yet implemented"); + } + + byte[] tmp = curLine; + curLine = prevLine; + prevLine = tmp; + } + } finally { + inflater.end(); + } + } + + private void copy (ByteBuffer buffer, byte[] curLine) { + buffer.put(curLine, 1, curLine.length - 1); + } + + private void copyRGBtoABGR (ByteBuffer buffer, byte[] curLine) { + if (transPixel != null) { + byte tr = transPixel[1]; + byte tg = transPixel[3]; + byte tb = transPixel[5]; + for (int i = 1, n = curLine.length; i < n; i += 3) { + byte r = curLine[i]; + byte g = curLine[i + 1]; + byte b = curLine[i + 2]; + byte a = (byte)0xFF; + if (r == tr && g == tg && b == tb) { + a = 0; + } + buffer.put(a).put(b).put(g).put(r); + } + } else { + for (int i = 1, n = curLine.length; i < n; i += 3) { + buffer.put((byte)0xFF).put(curLine[i + 2]).put(curLine[i + 1]).put(curLine[i]); + } + } + } + + private void copyRGBtoRGBA (ByteBuffer buffer, byte[] curLine) { + if (transPixel != null) { + byte tr = transPixel[1]; + byte tg = transPixel[3]; + byte tb = transPixel[5]; + for (int i = 1, n = curLine.length; i < n; i += 3) { + byte r = curLine[i]; + byte g = curLine[i + 1]; + byte b = curLine[i + 2]; + byte a = (byte)0xFF; + if (r == tr && g == tg && b == tb) { + a = 0; + } + buffer.put(r).put(g).put(b).put(a); + } + } else { + for (int i = 1, n = curLine.length; i < n; i += 3) { + buffer.put(curLine[i]).put(curLine[i + 1]).put(curLine[i + 2]).put((byte)0xFF); + } + } + } + + private void copyRGBtoBGRA (ByteBuffer buffer, byte[] curLine) { + if (transPixel != null) { + byte tr = transPixel[1]; + byte tg = transPixel[3]; + byte tb = transPixel[5]; + for (int i = 1, n = curLine.length; i < n; i += 3) { + byte r = curLine[i]; + byte g = curLine[i + 1]; + byte b = curLine[i + 2]; + byte a = (byte)0xFF; + if (r == tr && g == tg && b == tb) { + a = 0; + } + buffer.put(b).put(g).put(r).put(a); + } + } else { + for (int i = 1, n = curLine.length; i < n; i += 3) { + buffer.put(curLine[i + 2]).put(curLine[i + 1]).put(curLine[i]).put((byte)0xFF); + } + } + } + + private void copyRGBAtoABGR (ByteBuffer buffer, byte[] curLine) { + for (int i = 1, n = curLine.length; i < n; i += 4) { + buffer.put(curLine[i + 3]).put(curLine[i + 2]).put(curLine[i + 1]).put(curLine[i]); + } + } + + private void copyRGBAtoBGRA (ByteBuffer buffer, byte[] curLine) { + for (int i = 1, n = curLine.length; i < n; i += 4) { + buffer.put(curLine[i + 2]).put(curLine[i + 1]).put(curLine[i + 0]).put(curLine[i + 3]); + } + } + + private void copyRGBAtoRGB (ByteBuffer buffer, byte[] curLine) { + for (int i = 1, n = curLine.length; i < n; i += 4) { + buffer.put(curLine[i]).put(curLine[i + 1]).put(curLine[i + 2]); + } + } + + private void copyPALtoABGR (ByteBuffer buffer, byte[] curLine) { + if (paletteA != null) { + for (int i = 1, n = curLine.length; i < n; i += 1) { + int idx = curLine[i] & 255; + byte r = palette[idx * 3 + 0]; + byte g = palette[idx * 3 + 1]; + byte b = palette[idx * 3 + 2]; + byte a = paletteA[idx]; + buffer.put(a).put(b).put(g).put(r); + } + } else { + for (int i = 1, n = curLine.length; i < n; i += 1) { + int idx = curLine[i] & 255; + byte r = palette[idx * 3 + 0]; + byte g = palette[idx * 3 + 1]; + byte b = palette[idx * 3 + 2]; + byte a = (byte)0xFF; + buffer.put(a).put(b).put(g).put(r); + } + } + } + + private void copyPALtoRGBA (ByteBuffer buffer, byte[] curLine) { + if (paletteA != null) { + for (int i = 1, n = curLine.length; i < n; i += 1) { + int idx = curLine[i] & 255; + byte r = palette[idx * 3 + 0]; + byte g = palette[idx * 3 + 1]; + byte b = palette[idx * 3 + 2]; + byte a = paletteA[idx]; + buffer.put(r).put(g).put(b).put(a); + } + } else { + for (int i = 1, n = curLine.length; i < n; i += 1) { + int idx = curLine[i] & 255; + byte r = palette[idx * 3 + 0]; + byte g = palette[idx * 3 + 1]; + byte b = palette[idx * 3 + 2]; + byte a = (byte)0xFF; + buffer.put(r).put(g).put(b).put(a); + } + } + } + + private void copyPALtoBGRA (ByteBuffer buffer, byte[] curLine) { + if (paletteA != null) { + for (int i = 1, n = curLine.length; i < n; i += 1) { + int idx = curLine[i] & 255; + byte r = palette[idx * 3 + 0]; + byte g = palette[idx * 3 + 1]; + byte b = palette[idx * 3 + 2]; + byte a = paletteA[idx]; + buffer.put(b).put(g).put(r).put(a); + } + } else { + for (int i = 1, n = curLine.length; i < n; i += 1) { + int idx = curLine[i] & 255; + byte r = palette[idx * 3 + 0]; + byte g = palette[idx * 3 + 1]; + byte b = palette[idx * 3 + 2]; + byte a = (byte)0xFF; + buffer.put(b).put(g).put(r).put(a); + } + } + } + + private void expand4 (byte[] src, byte[] dst) { + for (int i = 1, n = dst.length; i < n; i += 2) { + int val = src[1 + (i >> 1)] & 255; + switch (n - i) { + default: + dst[i + 1] = (byte)(val & 15); + case 1: + dst[i] = (byte)(val >> 4); + } + } + } + + private void expand2 (byte[] src, byte[] dst) { + for (int i = 1, n = dst.length; i < n; i += 4) { + int val = src[1 + (i >> 2)] & 255; + switch (n - i) { + default: + dst[i + 3] = (byte)((val) & 3); + case 3: + dst[i + 2] = (byte)((val >> 2) & 3); + case 2: + dst[i + 1] = (byte)((val >> 4) & 3); + case 1: + dst[i] = (byte)((val >> 6)); + } + } + } + + private void expand1 (byte[] src, byte[] dst) { + for (int i = 1, n = dst.length; i < n; i += 8) { + int val = src[1 + (i >> 3)] & 255; + switch (n - i) { + default: + dst[i + 7] = (byte)((val) & 1); + case 7: + dst[i + 6] = (byte)((val >> 1) & 1); + case 6: + dst[i + 5] = (byte)((val >> 2) & 1); + case 5: + dst[i + 4] = (byte)((val >> 3) & 1); + case 4: + dst[i + 3] = (byte)((val >> 4) & 1); + case 3: + dst[i + 2] = (byte)((val >> 5) & 1); + case 2: + dst[i + 1] = (byte)((val >> 6) & 1); + case 1: + dst[i] = (byte)((val >> 7)); + } + } + } + + private void unfilter (byte[] curLine, byte[] prevLine) throws IOException { + switch (curLine[0]) { + case 0: // none + break; + case 1: + unfilterSub(curLine); + break; + case 2: + unfilterUp(curLine, prevLine); + break; + case 3: + unfilterAverage(curLine, prevLine); + break; + case 4: + unfilterPaeth(curLine, prevLine); + break; + default: + throw new IOException("invalide filter type in scanline: " + curLine[0]); + } + } + + private void unfilterSub (byte[] curLine) { + final int bpp = this.bytesPerPixel; + for (int i = bpp + 1, n = curLine.length; i < n; ++i) { + curLine[i] += curLine[i - bpp]; + } + } + + private void unfilterUp (byte[] curLine, byte[] prevLine) { + for (int i = 1, n = curLine.length; i < n; ++i) { + curLine[i] += prevLine[i]; + } + } + + private void unfilterAverage (byte[] curLine, byte[] prevLine) { + final int bpp = this.bytesPerPixel; + + int i; + for (i = 1; i <= bpp; ++i) { + curLine[i] += (byte)((prevLine[i] & 0xFF) >>> 1); + } + for (int n = curLine.length; i < n; ++i) { + curLine[i] += (byte)(((prevLine[i] & 0xFF) + (curLine[i - bpp] & 0xFF)) >>> 1); + } + } + + private void unfilterPaeth (byte[] curLine, byte[] prevLine) { + final int bpp = this.bytesPerPixel; + + int i; + for (i = 1; i <= bpp; ++i) { + curLine[i] += prevLine[i]; + } + for (int n = curLine.length; i < n; ++i) { + int a = curLine[i - bpp] & 255; + int b = prevLine[i] & 255; + int c = prevLine[i - bpp] & 255; + int p = a + b - c; + int pa = p - a; + if (pa < 0) pa = -pa; + int pb = p - b; + if (pb < 0) pb = -pb; + int pc = p - c; + if (pc < 0) pc = -pc; + if (pa <= pb && pa <= pc) + c = a; + else if (pb <= pc) c = b; + curLine[i] += (byte)c; + } + } + + private void readIHDR () throws IOException { + checkChunkLength(13); + readChunk(buffer, 0, 13); + width = readInt(buffer, 0); + height = readInt(buffer, 4); + bitdepth = buffer[8] & 255; + colorType = buffer[9] & 255; + + switch (colorType) { + case COLOR_GREYSCALE: + if (bitdepth != 8) { + throw new IOException("Unsupported bit depth: " + bitdepth); + } + bytesPerPixel = 1; + break; + case COLOR_GREYALPHA: + if (bitdepth != 8) { + throw new IOException("Unsupported bit depth: " + bitdepth); + } + bytesPerPixel = 2; + break; + case COLOR_TRUECOLOR: + if (bitdepth != 8) { + throw new IOException("Unsupported bit depth: " + bitdepth); + } + bytesPerPixel = 3; + break; + case COLOR_TRUEALPHA: + if (bitdepth != 8) { + throw new IOException("Unsupported bit depth: " + bitdepth); + } + bytesPerPixel = 4; + break; + case COLOR_INDEXED: + switch (bitdepth) { + case 8: + case 4: + case 2: + case 1: + bytesPerPixel = 1; + break; + default: + throw new IOException("Unsupported bit depth: " + bitdepth); + } + break; + default: + throw new IOException("unsupported color format: " + colorType); + } + + if (buffer[10] != 0) { + throw new IOException("unsupported compression method"); + } + if (buffer[11] != 0) { + throw new IOException("unsupported filtering method"); + } + if (buffer[12] != 0) { + throw new IOException("unsupported interlace method"); + } + } + + private void readPLTE () throws IOException { + int paletteEntries = chunkLength / 3; + if (paletteEntries < 1 || paletteEntries > 256 || (chunkLength % 3) != 0) { + throw new IOException("PLTE chunk has wrong length"); + } + palette = new byte[paletteEntries * 3]; + readChunk(palette, 0, palette.length); + } + + private void readtRNS () throws IOException { + switch (colorType) { + case COLOR_GREYSCALE: + checkChunkLength(2); + transPixel = new byte[2]; + readChunk(transPixel, 0, 2); + break; + case COLOR_TRUECOLOR: + checkChunkLength(6); + transPixel = new byte[6]; + readChunk(transPixel, 0, 6); + break; + case COLOR_INDEXED: + if (palette == null) { + throw new IOException("tRNS chunk without PLTE chunk"); + } + paletteA = new byte[palette.length / 3]; + Arrays.fill(paletteA, (byte)0xFF); + readChunk(paletteA, 0, paletteA.length); + break; + default: + // just ignore it + } + } + + private void closeChunk () throws IOException { + if (chunkRemaining > 0) { + // just skip the rest and the CRC + skip(chunkRemaining + 4); + } else { + readFully(buffer, 0, 4); + int expectedCrc = readInt(buffer, 0); + int computedCrc = (int)crc.getValue(); + if (computedCrc != expectedCrc) { + throw new IOException("Invalid CRC"); + } + } + chunkRemaining = 0; + chunkLength = 0; + chunkType = 0; + } + + private void openChunk () throws IOException { + readFully(buffer, 0, 8); + chunkLength = readInt(buffer, 0); + chunkType = readInt(buffer, 4); + chunkRemaining = chunkLength; + crc.reset(); + crc.update(buffer, 4, 4); // only chunkType + } + + private void openChunk (int expected) throws IOException { + openChunk(); + if (chunkType != expected) { + throw new IOException("Expected chunk: " + Integer.toHexString(expected)); + } + } + + private void checkChunkLength (int expected) throws IOException { + if (chunkLength != expected) { + throw new IOException("Chunk has wrong size"); + } + } + + private int readChunk (byte[] buffer, int offset, int length) throws IOException { + if (length > chunkRemaining) { + length = chunkRemaining; + } + readFully(buffer, offset, length); + crc.update(buffer, offset, length); + chunkRemaining -= length; + return length; + } + + private void refillInflater (Inflater inflater) throws IOException { + while (chunkRemaining == 0) { + closeChunk(); + openChunk(IDAT); + } + int read = readChunk(buffer, 0, buffer.length); + inflater.setInput(buffer, 0, read); + } + + private void readChunkUnzip (Inflater inflater, byte[] buffer, int offset, int length) throws IOException { + assert (buffer != this.buffer); + try { + do { + int read = inflater.inflate(buffer, offset, length); + if (read <= 0) { + if (inflater.finished()) { + throw new EOFException(); + } + if (inflater.needsInput()) { + refillInflater(inflater); + } else { + throw new IOException("Can't inflate " + length + " bytes"); + } + } else { + offset += read; + length -= read; + } + } while (length > 0); + } catch (DataFormatException ex) { + throw (IOException)(new IOException("inflate error").initCause(ex)); + } + } + + private void readFully (byte[] buffer, int offset, int length) throws IOException { + do { + int read = input.read(buffer, offset, length); + if (read < 0) { + throw new EOFException(); + } + offset += read; + length -= read; + } while (length > 0); + } + + private int readInt (byte[] buffer, int offset) { + return ((buffer[offset]) << 24) | ((buffer[offset + 1] & 255) << 16) | ((buffer[offset + 2] & 255) << 8) + | ((buffer[offset + 3] & 255)); + } + + private void skip (long amount) throws IOException { + while (amount > 0) { + long skipped = input.skip(amount); + if (skipped < 0) { + throw new EOFException(); + } + amount -= skipped; + } + } + + private static boolean checkSignatur (byte[] buffer) { + for (int i = 0; i < SIGNATURE.length; i++) { + if (buffer[i] != SIGNATURE[i]) { + return false; + } + } + return true; + } +} -- 2.11.0