1package com.jme3.scene.plugins.blender.textures;
2
3import com.jme3.math.ColorRGBA;
4import com.jme3.math.FastMath;
5import com.jme3.texture.Image.Format;
6import java.nio.ByteBuffer;
7import java.util.logging.Level;
8import java.util.logging.Logger;
9
10/**
11 * The class that stores the pixel values of a texture.
12 *
13 * @author Marcin Roguski (Kaelthas)
14 */
15public class TexturePixel implements Cloneable {
16	private static final Logger	LOGGER	= Logger.getLogger(TexturePixel.class.getName());
17
18	/** The pixel data. */
19	public float				intensity, red, green, blue, alpha;
20
21	/**
22	 * Copies the values from the given pixel.
23	 *
24	 * @param pixel
25	 *            the pixel that we read from
26	 */
27	public void fromPixel(TexturePixel pixel) {
28		this.intensity = pixel.intensity;
29		this.red = pixel.red;
30		this.green = pixel.green;
31		this.blue = pixel.blue;
32		this.alpha = pixel.alpha;
33	}
34
35	/**
36	 * Copies the values from the given color.
37	 *
38	 * @param colorRGBA
39	 *            the color that we read from
40	 */
41	public void fromColor(ColorRGBA colorRGBA) {
42		this.red = colorRGBA.r;
43		this.green = colorRGBA.g;
44		this.blue = colorRGBA.b;
45		this.alpha = colorRGBA.a;
46	}
47
48	/**
49	 * Copies the values from the given values.
50	 *
51	 * @param a
52	 *            the alpha value
53	 * @param r
54	 *            the red value
55	 * @param g
56	 *            the green value
57	 * @param b
58	 *            the blue value
59	 */
60	public void fromARGB8(float a, float r, float g, float b) {
61		this.alpha = a;
62		this.red = r;
63		this.green = g;
64		this.blue = b;
65	}
66
67	/**
68	 * Copies the values from the given integer that stores the ARGB8 data.
69	 *
70	 * @param argb8
71	 *            the data stored in an integer
72	 */
73	public void fromARGB8(int argb8) {
74		byte pixelValue = (byte) ((argb8 & 0xFF000000) >> 24);
75		this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
76		pixelValue = (byte) ((argb8 & 0xFF0000) >> 16);
77		this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
78		pixelValue = (byte) ((argb8 & 0xFF00) >> 8);
79		this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
80		pixelValue = (byte) (argb8 & 0xFF);
81		this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
82	}
83
84	/**
85	 * Copies the data from the given image.
86	 *
87	 * @param imageFormat
88	 *            the image format
89	 * @param data
90	 *            the image data
91	 * @param pixelIndex
92	 *            the index of the required pixel
93	 */
94	public void fromImage(Format imageFormat, ByteBuffer data, int pixelIndex) {
95		int firstByteIndex;
96		byte pixelValue;
97		switch (imageFormat) {
98			case ABGR8:
99				firstByteIndex = pixelIndex << 2;
100				pixelValue = data.get(firstByteIndex);
101				this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
102				pixelValue = data.get(firstByteIndex + 1);
103				this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
104				pixelValue = data.get(firstByteIndex + 2);
105				this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
106				pixelValue = data.get(firstByteIndex + 3);
107				this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
108				break;
109			case RGBA8:
110				firstByteIndex = pixelIndex << 2;
111				pixelValue = data.get(firstByteIndex);
112				this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
113				pixelValue = data.get(firstByteIndex + 1);
114				this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
115				pixelValue = data.get(firstByteIndex + 2);
116				this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
117				pixelValue = data.get(firstByteIndex + 3);
118				this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
119				break;
120			case BGR8:
121				firstByteIndex = pixelIndex * 3;
122				pixelValue = data.get(firstByteIndex);
123				this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
124				pixelValue = data.get(firstByteIndex + 1);
125				this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
126				pixelValue = data.get(firstByteIndex + 2);
127				this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
128				this.alpha = 1.0f;
129				break;
130			case RGB8:
131				firstByteIndex = pixelIndex * 3;
132				pixelValue = data.get(firstByteIndex);
133				this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
134				pixelValue = data.get(firstByteIndex + 1);
135				this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
136				pixelValue = data.get(firstByteIndex + 2);
137				this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
138				this.alpha = 1.0f;
139				break;
140			case Luminance8:
141				pixelValue = data.get(pixelIndex);
142				this.intensity = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
143				break;
144			default:
145				LOGGER.log(Level.FINEST, "Unknown type of texture: {0}. Black pixel used!", imageFormat);
146				this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
147		}
148	}
149
150	/**
151	 * Stores RGBA values in the given array.
152	 *
153	 * @param result
154	 *            the array to store values
155	 */
156	public void toRGBA(float[] result) {
157		result[0] = this.red;
158		result[1] = this.green;
159		result[2] = this.blue;
160		result[3] = this.alpha;
161	}
162
163	/**
164	 * Stores the data in the given table.
165	 *
166	 * @param result
167	 *            the result table
168	 */
169	public void toRGBA8(byte[] result) {
170		result[0] = (byte) (this.red * 255.0f);
171		result[1] = (byte) (this.green * 255.0f);
172		result[2] = (byte) (this.blue * 255.0f);
173		result[3] = (byte) (this.alpha * 255.0f);
174	}
175
176	/**
177	 * Stores the pixel values in the integer.
178	 *
179	 * @return the integer that stores the pixel values
180	 */
181	public int toARGB8() {
182		int result = 0;
183		int b = (int) (this.alpha * 255.0f);
184		result |= b << 24;
185		b = (int) (this.red * 255.0f);
186		result |= b << 16;
187		b = (int) (this.green * 255.0f);
188		result |= b << 8;
189		b = (int) (this.blue * 255.0f);
190		result |= b;
191		return result;
192	}
193
194	/**
195	 * Merges two pixels (adds the values of each color).
196	 *
197	 * @param pixel
198	 *            the pixel we merge with
199	 */
200	public void merge(TexturePixel pixel) {
201		float oneMinusAlpha = 1 - pixel.alpha;
202		this.red = oneMinusAlpha * this.red + pixel.alpha * pixel.red;
203		this.green = oneMinusAlpha * this.green + pixel.alpha * pixel.green;
204		this.blue = oneMinusAlpha * this.blue + pixel.alpha * pixel.blue;
205		// alpha should be always 1.0f as a result
206	}
207
208	/**
209	 * This method negates the colors.
210	 */
211	public void negate() {
212		this.red = 1.0f - this.red;
213		this.green = 1.0f - this.green;
214		this.blue = 1.0f - this.blue;
215		this.alpha = 1.0f - this.alpha;
216	}
217
218	/**
219	 * This method clears the pixel values.
220	 */
221	public void clear() {
222		this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
223	}
224
225	/**
226	 * This method adds the calues of the given pixel to the current pixel.
227	 *
228	 * @param pixel
229	 *            the pixel we add
230	 */
231	public void add(TexturePixel pixel) {
232		this.red += pixel.red;
233		this.green += pixel.green;
234		this.blue += pixel.blue;
235		this.alpha += pixel.alpha;
236		this.intensity += pixel.intensity;
237	}
238
239	/**
240	 * This method multiplies the values of the given pixel by the given value.
241	 *
242	 * @param value
243	 *            multiplication factor
244	 */
245	public void mult(float value) {
246		this.red *= value;
247		this.green *= value;
248		this.blue *= value;
249		this.alpha *= value;
250		this.intensity *= value;
251	}
252
253	/**
254	 * This method divides the values of the given pixel by the given value.
255	 * ATTENTION! Beware of the zero value. This will cause you NaN's in the
256	 * pixel values.
257	 *
258	 * @param value
259	 *            division factor
260	 */
261	public void divide(float value) {
262		this.red /= value;
263		this.green /= value;
264		this.blue /= value;
265		this.alpha /= value;
266		this.intensity /= value;
267	}
268
269	/**
270	 * This method clamps the pixel values to the given borders.
271	 *
272	 * @param min
273	 *            the minimum value
274	 * @param max
275	 *            the maximum value
276	 */
277	public void clamp(float min, float max) {
278		this.red = FastMath.clamp(this.red, min, max);
279		this.green = FastMath.clamp(this.green, min, max);
280		this.blue = FastMath.clamp(this.blue, min, max);
281		this.alpha = FastMath.clamp(this.alpha, min, max);
282		this.intensity = FastMath.clamp(this.intensity, min, max);
283	}
284
285	@Override
286	public Object clone() throws CloneNotSupportedException {
287		return super.clone();
288	}
289
290	@Override
291	public String toString() {
292		return "[" + red + ", " + green + ", " + blue + ", " + alpha + " {" + intensity + "}]";
293	}
294}
295