1 package org.newdawn.slick.opengl;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.nio.ByteBuffer;
6 import org.lwjgl.BufferUtils;
7 import org.newdawn.slick.util.Log;
8
9 import javax.annotation.Nullable;
10
11
12
13
14
15
16 public class PNGImageData implements LoadableImageData {
17
18 private int width;
19
20 private int height;
21
22 private int texHeight;
23
24 private int texWidth;
25
26 private Format format;
27
28 private ByteBuffer scratch;
29
30
31
32
33 public Format getFormat() {
34 return format;
35 }
36
37
38
39
40 public ByteBuffer getImageBufferData() {
41 return scratch;
42 }
43
44
45
46
47 public int getTexHeight() {
48 return texHeight;
49 }
50
51
52
53
54 public int getTexWidth() {
55 return texWidth;
56 }
57
58
59
60
61 public ByteBuffer loadImage(InputStream fis) throws IOException {
62 return loadImage(fis, false, null);
63 }
64
65
66
67
68 public ByteBuffer loadImage(InputStream fis, boolean flipped, @Nullable int[] transparent) throws IOException {
69 return loadImage(fis, flipped, false, transparent);
70 }
71
72
73
74
75 public ByteBuffer loadImage(InputStream fis, boolean flipped, boolean forceAlpha, @Nullable int[] transparent) throws IOException {
76 if (transparent != null) {
77 forceAlpha = true;
78 }
79
80 PNGDecoder decoder = new PNGDecoder(fis);
81
82 width = decoder.getWidth();
83 height = decoder.getHeight();
84 texWidth = get2Fold(width);
85 texHeight = get2Fold(height);
86
87 final PNGDecoder.Format decoderFormat;
88 if (forceAlpha) {
89 if (decoder.isRGB()) {
90 decoderFormat = decoder.decideTextureFormat(PNGDecoder.Format.RGBA);
91 } else {
92 decoderFormat = decoder.decideTextureFormat(PNGDecoder.Format.LUMINANCE_ALPHA);
93 }
94 } else {
95 decoderFormat = decoder.decideTextureFormat(PNGDecoder.Format.LUMINANCE);
96 }
97
98 switch (decoderFormat) {
99 case RGB:
100 format = Format.RGB;
101 break;
102 case RGBA:
103 format = Format.RGBA;
104 break;
105 case BGRA:
106 format = Format.BGRA;
107 break;
108 case LUMINANCE:
109 format = Format.GRAY;
110 break;
111 case LUMINANCE_ALPHA:
112 format = Format.GRAYALPHA;
113 break;
114 default:
115 throw new IOException("Unsupported Image format.");
116 }
117
118 int perPixel = format.getColorComponents();
119
120
121 scratch = BufferUtils.createByteBuffer(texWidth * texHeight * perPixel);
122
123 if (flipped) {
124 decoder.decodeFlipped(scratch, texWidth * perPixel, decoderFormat);
125 } else {
126 decoder.decode(scratch, texWidth * perPixel, decoderFormat);
127 }
128
129 if (height < texHeight-1) {
130 int topOffset = (texHeight-1) * (texWidth*perPixel);
131 int bottomOffset = (height-1) * (texWidth*perPixel);
132 for (int x=0;x<texWidth;x++) {
133 for (int i=0;i<perPixel;i++) {
134 scratch.put(topOffset+x+i, scratch.get(x+i));
135 scratch.put(bottomOffset+(texWidth*perPixel)+x+i, scratch.get(bottomOffset+x+i));
136 }
137 }
138 }
139 if (width < texWidth-1) {
140 for (int y=0;y<texHeight;y++) {
141 for (int i=0;i<perPixel;i++) {
142 scratch.put(((y+1)*(texWidth*perPixel))-perPixel+i, scratch.get(y*(texWidth*perPixel)+i));
143 scratch.put((y*(texWidth*perPixel))+(width*perPixel)+i, scratch.get((y*(texWidth*perPixel))+((width-1)*perPixel)+i));
144 }
145 }
146 }
147
148 scratch.position(0);
149
150 if (transparent != null) {
151
152 final int components = format.getColorComponents();
153
154 if (transparent.length != components - 1) {
155 Log.warn("The amount of color components of the transparent color does not fit the number of color components of the actual image.");
156 }
157
158 if (transparent.length < components - 1) {
159 Log.error("Failed to apply transparent color, not enough color values in color definition.");
160 } else {
161
162 final int size = texWidth * texHeight * components;
163 boolean match;
164
165 for (int i = 0; i < size; i += components) {
166 match = true;
167 for (int c = 0; c < components - 1; c++) {
168 if (toInt(scratch.get(i + c)) != transparent[c]) {
169 match = false;
170 break;
171 }
172 }
173 if (match) {
174 scratch.put(i + components - 1, (byte) 0);
175 }
176 }
177 }
178 }
179
180 scratch.position(0);
181
182 return scratch;
183 }
184
185
186
187
188
189
190
191 private int toInt(byte b) {
192 if (b < 0) {
193 return 256+b;
194 }
195
196 return b;
197 }
198
199
200
201
202
203
204
205 private int get2Fold(int fold) {
206 int ret = 2;
207 while (ret < fold) {
208 ret *= 2;
209 }
210 return ret;
211 }
212
213
214
215
216 public void configureEdging(boolean edging) {
217 }
218
219 public int getWidth() {
220 return width;
221 }
222
223 public int getHeight() {
224 return height;
225 }
226 }
227