• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.graphics;
18 
19 import java.nio.Buffer;
20 import java.nio.IntBuffer;
21 import java.util.HashMap;
22 import java.util.Map;
23 
24 import com.badlogic.gdx.backends.gwt.GwtFileHandle;
25 import com.badlogic.gdx.files.FileHandle;
26 import com.badlogic.gdx.utils.BufferUtils;
27 import com.badlogic.gdx.utils.Disposable;
28 import com.badlogic.gdx.utils.GdxRuntimeException;
29 import com.google.gwt.canvas.client.Canvas;
30 import com.google.gwt.canvas.dom.client.CanvasPixelArray;
31 import com.google.gwt.canvas.dom.client.Context2d;
32 import com.google.gwt.canvas.dom.client.Context2d.Composite;
33 import com.google.gwt.dom.client.CanvasElement;
34 import com.google.gwt.dom.client.ImageElement;
35 
36 public class Pixmap implements Disposable {
37 	public static Map<Integer, Pixmap> pixmaps = new HashMap<Integer, Pixmap>();
38 	static int nextId = 0;
39 
40 	/** Different pixel formats.
41 	 *
42 	 * @author mzechner */
43 	public enum Format {
44 		Alpha, Intensity, LuminanceAlpha, RGB565, RGBA4444, RGB888, RGBA8888;
45 
toGlFormat(Format format)46 		public static int toGlFormat (Format format) {
47 			if (format == Alpha) return GL20.GL_ALPHA;
48 			if (format == Intensity) return GL20.GL_ALPHA;
49 			if (format == LuminanceAlpha) return GL20.GL_LUMINANCE_ALPHA;
50 			if (format == RGB565) return GL20.GL_RGB;
51 			if (format == RGB888) return GL20.GL_RGB;
52 			if (format == RGBA4444) return GL20.GL_RGBA;
53 			if (format == RGBA8888) return GL20.GL_RGBA;
54 			throw new GdxRuntimeException("unknown format: " + format);
55 		}
56 
toGlType(Format format)57 		public static int toGlType (Format format) {
58 			if (format == Alpha) return GL20.GL_UNSIGNED_BYTE;
59 			if (format == Intensity) return GL20.GL_UNSIGNED_BYTE;
60 			if (format == LuminanceAlpha) return GL20.GL_UNSIGNED_BYTE;
61 			if (format == RGB565) return GL20.GL_UNSIGNED_SHORT_5_6_5;
62 			if (format == RGB888) return GL20.GL_UNSIGNED_BYTE;
63 			if (format == RGBA4444) return GL20.GL_UNSIGNED_SHORT_4_4_4_4;
64 			if (format == RGBA8888) return GL20.GL_UNSIGNED_BYTE;
65 			throw new GdxRuntimeException("unknown format: " + format);
66 		}
67 	}
68 
69 	/** Blending functions to be set with {@link Pixmap#setBlending}.
70 	 * @author mzechner */
71 	public enum Blending {
72 		None, SourceOver
73 	}
74 
75 	/** Filters to be used with {@link Pixmap#drawPixmap(Pixmap, int, int, int, int, int, int, int, int)}.
76 	 *
77 	 * @author mzechner */
78 	public enum Filter {
79 		NearestNeighbour, BiLinear
80 	}
81 
82 	int width;
83 	int height;
84 	Format format;
85 	Canvas canvas;
86 	Context2d context;
87 	int id;
88 	IntBuffer buffer;
89 	int r = 255, g = 255, b = 255;
90 	float a;
91 	String color = make(r, g, b, a);
92 	static String clearColor = make(255, 255, 255, 1.0f);
93 	static Blending blending;
94 	CanvasPixelArray pixels;
95 
Pixmap(FileHandle file)96 	public Pixmap (FileHandle file) {
97 		GwtFileHandle gwtFile = (GwtFileHandle)file;
98 		ImageElement img = gwtFile.preloader.images.get(file.path());
99 		if (img == null) throw new GdxRuntimeException("Couldn't load image '" + file.path() + "', file does not exist");
100 		create(img.getWidth(), img.getHeight(), Format.RGBA8888);
101 		context.setGlobalCompositeOperation(Composite.COPY);
102 		context.drawImage(img, 0, 0);
103 		context.setGlobalCompositeOperation(getComposite());
104 	}
105 
getContext()106 	public Context2d getContext() {
107 		return context;
108 	}
109 
getComposite()110 	private static Composite getComposite () {
111 		return Composite.SOURCE_OVER;
112 	}
113 
Pixmap(ImageElement img)114 	public Pixmap (ImageElement img) {
115 		create(img.getWidth(), img.getHeight(), Format.RGBA8888);
116 		context.drawImage(img, 0, 0);
117 	}
118 
Pixmap(int width, int height, Format format)119 	public Pixmap (int width, int height, Format format) {
120 		create(width, height, format);
121 	}
122 
create(int width, int height, Format format2)123 	private void create (int width, int height, Format format2) {
124 		this.width = width;
125 		this.height = height;
126 		this.format = Format.RGBA8888;
127 		canvas = Canvas.createIfSupported();
128 		canvas.getCanvasElement().setWidth(width);
129 		canvas.getCanvasElement().setHeight(height);
130 		context = canvas.getContext2d();
131 		context.setGlobalCompositeOperation(getComposite());
132 		buffer = BufferUtils.newIntBuffer(1);
133 		id = nextId++;
134 		buffer.put(0, id);
135 		pixmaps.put(id, this);
136 	}
137 
make(int r2, int g2, int b2, float a2)138 	public static String make (int r2, int g2, int b2, float a2) {
139 		return "rgba(" + r2 + "," + g2 + "," + b2 + "," + a2 + ")";
140 	}
141 
142 	/** Sets the type of {@link Blending} to be used for all operations. Default is {@link Blending#SourceOver}.
143 	 * @param blending the blending type */
setBlending(Blending blending)144 	public static void setBlending (Blending blending) {
145 		Pixmap.blending = blending;
146 		Composite composite = getComposite();
147 		for (Pixmap pixmap : pixmaps.values()) {
148 			pixmap.context.setGlobalCompositeOperation(composite);
149 		}
150 	}
151 
152 	/** @return the currently set {@link Blending} */
getBlending()153 	public static Blending getBlending () {
154 		return blending;
155 	}
156 
157 	/** Sets the type of interpolation {@link Filter} to be used in conjunction with
158 	 * {@link Pixmap#drawPixmap(Pixmap, int, int, int, int, int, int, int, int)}.
159 	 * @param filter the filter. */
setFilter(Filter filter)160 	public static void setFilter (Filter filter) {
161 	}
162 
getFormat()163 	public Format getFormat () {
164 		return format;
165 	}
166 
getGLInternalFormat()167 	public int getGLInternalFormat () {
168 		return GL20.GL_RGBA;
169 	}
170 
getGLFormat()171 	public int getGLFormat () {
172 		return GL20.GL_RGBA;
173 	}
174 
getGLType()175 	public int getGLType () {
176 		return GL20.GL_UNSIGNED_BYTE;
177 	}
178 
getWidth()179 	public int getWidth () {
180 		return width;
181 	}
182 
getHeight()183 	public int getHeight () {
184 		return height;
185 	}
186 
getPixels()187 	public Buffer getPixels () {
188 		return buffer;
189 	}
190 
191 	@Override
dispose()192 	public void dispose () {
193 		pixmaps.remove(id);
194 	}
195 
getCanvasElement()196 	public CanvasElement getCanvasElement () {
197 		return canvas.getCanvasElement();
198 	}
199 
200 	/** Sets the color for the following drawing operations
201 	 * @param color the color, encoded as RGBA8888 */
setColor(int color)202 	public void setColor (int color) {
203 		r = (color >>> 24) & 0xff;
204 		g = (color >>> 16) & 0xff;
205 		b = (color >>> 8) & 0xff;
206 		a = (color & 0xff) / 255f;
207 		this.color = make(r, g, b, a);
208 		context.setFillStyle(this.color);
209 		context.setStrokeStyle(this.color);
210 	}
211 
212 	/** Sets the color for the following drawing operations.
213 	 *
214 	 * @param r The red component.
215 	 * @param g The green component.
216 	 * @param b The blue component.
217 	 * @param a The alpha component. */
setColor(float r, float g, float b, float a)218 	public void setColor (float r, float g, float b, float a) {
219 		this.r = (int)(r * 255);
220 		this.g = (int)(g * 255);
221 		this.b = (int)(b * 255);
222 		this.a = a;
223 		color = make(this.r, this.g, this.b, this.a);
224 		context.setFillStyle(color);
225 		context.setStrokeStyle(this.color);
226 	}
227 
228 	/** Sets the color for the following drawing operations.
229 	 * @param color The color. */
setColor(Color color)230 	public void setColor (Color color) {
231 		setColor(color.r, color.g, color.b, color.a);
232 	}
233 
234 	/** Fills the complete bitmap with the currently set color. */
fill()235 	public void fill () {
236 		context.clearRect(0, 0, getWidth(), getHeight());
237 		rectangle(0, 0, getWidth(), getHeight(), DrawType.FILL);
238 	}
239 
240 // /**
241 // * Sets the width in pixels of strokes.
242 // *
243 // * @param width The stroke width in pixels.
244 // */
245 // public void setStrokeWidth (int width);
246 
247 	/** Draws a line between the given coordinates using the currently set color.
248 	 *
249 	 * @param x The x-coodinate of the first point
250 	 * @param y The y-coordinate of the first point
251 	 * @param x2 The x-coordinate of the first point
252 	 * @param y2 The y-coordinate of the first point */
drawLine(int x, int y, int x2, int y2)253 	public void drawLine (int x, int y, int x2, int y2) {
254 		line(x, y, x2, y2, DrawType.STROKE);
255 	}
256 
257 	/** Draws a rectangle outline starting at x, y extending by width to the right and by height downwards (y-axis points downwards)
258 	 * using the current color.
259 	 *
260 	 * @param x The x coordinate
261 	 * @param y The y coordinate
262 	 * @param width The width in pixels
263 	 * @param height The height in pixels */
drawRectangle(int x, int y, int width, int height)264 	public void drawRectangle (int x, int y, int width, int height) {
265 		rectangle(x, y, width, height, DrawType.STROKE);
266 	}
267 
268 	/** Draws an area form another Pixmap to this Pixmap.
269 	 *
270 	 * @param pixmap The other Pixmap
271 	 * @param x The target x-coordinate (top left corner)
272 	 * @param y The target y-coordinate (top left corner) */
drawPixmap(Pixmap pixmap, int x, int y)273 	public void drawPixmap (Pixmap pixmap, int x, int y) {
274 		CanvasElement image = pixmap.getCanvasElement();
275 		image(image, 0, 0, image.getWidth(), image.getHeight(), x, y, image.getWidth(), image.getHeight());
276 	}
277 
278 	/** Draws an area form another Pixmap to this Pixmap.
279 	 *
280 	 * @param pixmap The other Pixmap
281 	 * @param x The target x-coordinate (top left corner)
282 	 * @param y The target y-coordinate (top left corner)
283 	 * @param srcx The source x-coordinate (top left corner)
284 	 * @param srcy The source y-coordinate (top left corner);
285 	 * @param srcWidth The width of the area form the other Pixmap in pixels
286 	 * @param srcHeight The height of the area form the other Pixmap in pixles */
drawPixmap(Pixmap pixmap, int x, int y, int srcx, int srcy, int srcWidth, int srcHeight)287 	public void drawPixmap (Pixmap pixmap, int x, int y, int srcx, int srcy, int srcWidth, int srcHeight) {
288 		CanvasElement image = pixmap.getCanvasElement();
289 		image(image, srcx, srcy, srcWidth, srcHeight, x, y, srcWidth, srcHeight);
290 	}
291 
292 	/** Draws an area form another Pixmap to this Pixmap. This will automatically scale and stretch the source image to the
293 	 * specified target rectangle. Use {@link Pixmap#setFilter(Filter)} to specify the type of filtering to be used (nearest
294 	 * neighbour or bilinear).
295 	 *
296 	 * @param pixmap The other Pixmap
297 	 * @param srcx The source x-coordinate (top left corner)
298 	 * @param srcy The source y-coordinate (top left corner);
299 	 * @param srcWidth The width of the area form the other Pixmap in pixels
300 	 * @param srcHeight The height of the area form the other Pixmap in pixles
301 	 * @param dstx The target x-coordinate (top left corner)
302 	 * @param dsty The target y-coordinate (top left corner)
303 	 * @param dstWidth The target width
304 	 * @param dstHeight the target height */
drawPixmap(Pixmap pixmap, int srcx, int srcy, int srcWidth, int srcHeight, int dstx, int dsty, int dstWidth, int dstHeight)305 	public void drawPixmap (Pixmap pixmap, int srcx, int srcy, int srcWidth, int srcHeight, int dstx, int dsty, int dstWidth,
306 		int dstHeight) {
307 		image(pixmap.getCanvasElement(), srcx, srcy, srcWidth, srcHeight, dstx, dsty, dstWidth, dstHeight);
308 	}
309 
310 	/** Fills a rectangle starting at x, y extending by width to the right and by height downwards (y-axis points downwards) using
311 	 * the current color.
312 	 *
313 	 * @param x The x coordinate
314 	 * @param y The y coordinate
315 	 * @param width The width in pixels
316 	 * @param height The height in pixels */
fillRectangle(int x, int y, int width, int height)317 	public void fillRectangle (int x, int y, int width, int height) {
318 		rectangle(x, y, width, height, DrawType.FILL);
319 	}
320 
321 	/** Draws a circle outline with the center at x,y and a radius using the current color and stroke width.
322 	 *
323 	 * @param x The x-coordinate of the center
324 	 * @param y The y-coordinate of the center
325 	 * @param radius The radius in pixels */
drawCircle(int x, int y, int radius)326 	public void drawCircle (int x, int y, int radius) {
327 		circle(x, y, radius, DrawType.STROKE);
328 	}
329 
330 	/** Fills a circle with the center at x,y and a radius using the current color.
331 	 *
332 	 * @param x The x-coordinate of the center
333 	 * @param y The y-coordinate of the center
334 	 * @param radius The radius in pixels */
fillCircle(int x, int y, int radius)335 	public void fillCircle (int x, int y, int radius) {
336 		circle(x, y, radius, DrawType.FILL);
337 	}
338 
339 	/** Fills a triangle with vertices at x1,y1 and x2,y2 and x3,y3 using the current color.
340 	 *
341 	 * @param x1 The x-coordinate of vertex 1
342 	 * @param y1 The y-coordinate of vertex 1
343 	 * @param x2 The x-coordinate of vertex 2
344 	 * @param y2 The y-coordinate of vertex 2
345 	 * @param x3 The x-coordinate of vertex 3
346 	 * @param y3 The y-coordinate of vertex 3 */
fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3)347 	public void fillTriangle (int x1, int y1, int x2, int y2, int x3, int y3) {
348 		triangle(x1, y1, x2, y2, x3, y3, DrawType.FILL);
349 	}
350 
351 	/** Returns the 32-bit RGBA8888 value of the pixel at x, y. For Alpha formats the RGB components will be one.
352 	 *
353 	 * @param x The x-coordinate
354 	 * @param y The y-coordinate
355 	 * @return The pixel color in RGBA8888 format. */
getPixel(int x, int y)356 	public int getPixel (int x, int y) {
357 		if (pixels == null) pixels = context.getImageData(0, 0, width, height).getData();
358 		int i = x * 4 + y * width * 4;
359 		int r = pixels.get(i + 0) & 0xff;
360 		int g = pixels.get(i + 1) & 0xff;
361 		int b = pixels.get(i + 2) & 0xff;
362 		int a = pixels.get(i + 3) & 0xff;
363 		return (r << 24) | (g << 16) | (b << 8) | (a);
364 	}
365 
366 	/** Draws a pixel at the given location with the current color.
367 	 *
368 	 * @param x the x-coordinate
369 	 * @param y the y-coordinate */
drawPixel(int x, int y)370 	public void drawPixel (int x, int y) {
371 		rectangle(x, y, 1, 1, DrawType.FILL);
372 	}
373 
374 	/** Draws a pixel at the given location with the given color.
375 	 *
376 	 * @param x the x-coordinate
377 	 * @param y the y-coordinate
378 	 * @param color the color in RGBA8888 format. */
drawPixel(int x, int y, int color)379 	public void drawPixel (int x, int y, int color) {
380 		setColor(color);
381 		drawPixel(x, y);
382 	}
383 
circle(int x, int y, int radius, DrawType drawType)384 	private void circle (int x, int y, int radius, DrawType drawType) {
385 		if (blending == Blending.None) {
386 			context.setFillStyle(clearColor);
387 			context.setStrokeStyle(clearColor);
388 			context.setGlobalCompositeOperation("destination-out");
389 			context.beginPath();
390 			context.arc(x, y, radius, 0, 2 * Math.PI, false);
391 			fillOrStrokePath(drawType);
392 			context.closePath();
393 			context.setFillStyle(color);
394 			context.setStrokeStyle(color);
395 			context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
396 		}
397 		context.beginPath();
398 		context.arc(x, y, radius, 0, 2 * Math.PI, false);
399 		fillOrStrokePath(drawType);
400 		context.closePath();
401 		pixels = null;
402 	}
403 
line(int x, int y, int x2, int y2, DrawType drawType)404 	private void line(int x, int y, int x2, int y2, DrawType drawType) {
405 		if (blending == Blending.None) {
406 			context.setFillStyle(clearColor);
407 			context.setStrokeStyle(clearColor);
408 			context.setGlobalCompositeOperation("destination-out");
409 			context.beginPath();
410 			context.moveTo(x, y);
411 			context.lineTo(x2, y2);
412 			fillOrStrokePath(drawType);
413 			context.closePath();
414 			context.setFillStyle(color);
415 			context.setStrokeStyle(color);
416 			context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
417 		}
418 		context.beginPath();
419 		context.moveTo(x, y);
420 		context.lineTo(x2, y2);
421 		fillOrStrokePath(drawType);
422 		context.closePath();
423 		pixels = null;
424 	}
425 
rectangle(int x, int y, int width, int height, DrawType drawType)426 	private void rectangle(int x, int y, int width, int height, DrawType drawType) {
427 		if (blending == Blending.None) {
428 			context.setFillStyle(clearColor);
429 			context.setStrokeStyle(clearColor);
430 			context.setGlobalCompositeOperation("destination-out");
431 			context.beginPath();
432 			context.rect(x, y, width, height);
433 			fillOrStrokePath(drawType);
434 			context.closePath();
435 			context.setFillStyle(color);
436 			context.setStrokeStyle(color);
437 			context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
438 		}
439 		context.beginPath();
440 		context.rect(x, y, width, height);
441 		fillOrStrokePath(drawType);
442 		context.closePath();
443 		pixels = null;
444 	}
445 
triangle(int x1, int y1, int x2, int y2, int x3, int y3, DrawType drawType)446 	private void triangle(int x1, int y1, int x2, int y2, int x3, int y3, DrawType drawType) {
447 		if (blending == Blending.None) {
448 			context.setFillStyle(clearColor);
449 			context.setStrokeStyle(clearColor);
450 			context.setGlobalCompositeOperation("destination-out");
451 			context.beginPath();
452 			context.moveTo(x1,y1);
453 			context.lineTo(x2,y2);
454 			context.lineTo(x3,y3);
455 			context.lineTo(x1,y1);
456 			fillOrStrokePath(drawType);
457 			context.closePath();
458 			context.setFillStyle(color);
459 			context.setStrokeStyle(color);
460 			context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
461 		}
462 		context.beginPath();
463 		context.moveTo(x1,y1);
464 		context.lineTo(x2,y2);
465 		context.lineTo(x3,y3);
466 		context.lineTo(x1,y1);
467 		fillOrStrokePath(drawType);
468 		context.closePath();
469 		pixels = null;
470 	}
471 
image(CanvasElement image, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight)472 	private void image (CanvasElement image, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight) {
473 		if (blending == Blending.None) {
474 			context.setFillStyle(clearColor);
475 			context.setStrokeStyle(clearColor);
476 			context.setGlobalCompositeOperation("destination-out");
477 			context.beginPath();
478 			context.rect(dstX, dstY, dstWidth, dstHeight);
479 			fillOrStrokePath(DrawType.FILL);
480 			context.closePath();
481 			context.setFillStyle(color);
482 			context.setStrokeStyle(color);
483 			context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
484 		}
485 		context.drawImage(image, srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight);
486 		pixels = null;
487 	}
488 
fillOrStrokePath(DrawType drawType)489 	private void fillOrStrokePath(DrawType drawType) {
490 		switch (drawType) {
491 			case FILL:
492 				context.fill();
493 				break;
494 			case STROKE:
495 				context.stroke();
496 				break;
497 		}
498 	}
499 
500 	private enum DrawType {
501 		FILL, STROKE
502 	}
503 
504 }
505