com.badlogic.gdx.graphics.Pixmap Maven / Gradle / Ivy
Show all versions of vtm-web Show documentation
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* 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.graphics;
import com.badlogic.gdx.backends.gwt.GwtFileHandle;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.BufferUtils;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.CanvasPixelArray;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.Context2d.Composite;
import com.google.gwt.dom.client.CanvasElement;
import com.google.gwt.dom.client.ImageElement;
import java.nio.Buffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
public class Pixmap implements Disposable {
public static Map pixmaps = new HashMap();
static int nextId = 0;
/**
* Different pixel formats.
*
* @author mzechner
*/
public enum Format {
Alpha, Intensity, LuminanceAlpha, RGB565, RGBA4444, RGB888, RGBA8888;
public static int toGlFormat(Format format) {
if (format == Alpha) return GL20.GL_ALPHA;
if (format == Intensity) return GL20.GL_ALPHA;
if (format == LuminanceAlpha) return GL20.GL_LUMINANCE_ALPHA;
if (format == RGB565) return GL20.GL_RGB;
if (format == RGB888) return GL20.GL_RGB;
if (format == RGBA4444) return GL20.GL_RGBA;
if (format == RGBA8888) return GL20.GL_RGBA;
throw new GdxRuntimeException("unknown format: " + format);
}
public static int toGlType(Format format) {
if (format == Alpha) return GL20.GL_UNSIGNED_BYTE;
if (format == Intensity) return GL20.GL_UNSIGNED_BYTE;
if (format == LuminanceAlpha) return GL20.GL_UNSIGNED_BYTE;
if (format == RGB565) return GL20.GL_UNSIGNED_SHORT_5_6_5;
if (format == RGB888) return GL20.GL_UNSIGNED_BYTE;
if (format == RGBA4444) return GL20.GL_UNSIGNED_SHORT_4_4_4_4;
if (format == RGBA8888) return GL20.GL_UNSIGNED_BYTE;
throw new GdxRuntimeException("unknown format: " + format);
}
}
/**
* Blending functions to be set with {@link Pixmap#setBlending}.
*
* @author mzechner
*/
public enum Blending {
None, SourceOver
}
/**
* Filters to be used with {@link Pixmap#drawPixmap(Pixmap, int, int, int, int, int, int, int, int)}.
*
* @author mzechner
*/
public enum Filter {
NearestNeighbour, BiLinear
}
int width;
int height;
Format format;
Canvas canvas;
Context2d context;
int id;
IntBuffer buffer;
int r = 255, g = 255, b = 255;
float a;
String color = make(r, g, b, a);
static String clearColor = make(255, 255, 255, 1.0f);
static Blending blending;
CanvasPixelArray pixels;
private ImageElement imageElement;
public Pixmap(FileHandle file) {
this(((GwtFileHandle) file).preloader.images.get(file.path()));
if (imageElement == null)
throw new GdxRuntimeException("Couldn't load image '" + file.path() + "', file does not exist");
}
public Context2d getContext() {
ensureCanvasExists();
return context;
}
private static Composite getComposite() {
return Composite.SOURCE_OVER;
}
public Pixmap(ImageElement img) {
this(-1, -1, img);
}
public Pixmap(int width, int height, Format format) {
this(width, height, (ImageElement) null);
}
private Pixmap(int width, int height, ImageElement imageElement) {
this.imageElement = imageElement;
this.width = imageElement != null ? imageElement.getWidth() : width;
this.height = imageElement != null ? imageElement.getHeight() : height;
buffer = BufferUtils.newIntBuffer(1);
id = nextId++;
buffer.put(0, id);
pixmaps.put(id, this);
}
private void create() {
canvas = Canvas.createIfSupported();
canvas.getCanvasElement().setWidth(width);
canvas.getCanvasElement().setHeight(height);
context = canvas.getContext2d();
context.setGlobalCompositeOperation(getComposite());
}
public static String make(int r2, int g2, int b2, float a2) {
return "rgba(" + r2 + "," + g2 + "," + b2 + "," + a2 + ")";
}
/**
* Sets the type of {@link Blending} to be used for all operations. Default is {@link Blending#SourceOver}.
*
* @param blending the blending type
*/
public static void setBlending(Blending blending) {
Pixmap.blending = blending;
Composite composite = getComposite();
for (Pixmap pixmap : pixmaps.values()) {
pixmap.ensureCanvasExists();
pixmap.context.setGlobalCompositeOperation(composite);
}
}
/**
* @return the currently set {@link Blending}
*/
public static Blending getBlending() {
return blending;
}
/**
* Sets the type of interpolation {@link Filter} to be used in conjunction with
* {@link Pixmap#drawPixmap(Pixmap, int, int, int, int, int, int, int, int)}.
*
* @param filter the filter.
*/
public static void setFilter(Filter filter) {
}
public Format getFormat() {
return format;
}
public int getGLInternalFormat() {
return GL20.GL_RGBA;
}
public int getGLFormat() {
return GL20.GL_RGBA;
}
public int getGLType() {
return GL20.GL_UNSIGNED_BYTE;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Buffer getPixels() {
return buffer;
}
@Override
public void dispose() {
pixmaps.remove(id);
}
public CanvasElement getCanvasElement() {
ensureCanvasExists();
return canvas.getCanvasElement();
}
private void ensureCanvasExists() {
if (canvas == null) {
create();
if (imageElement != null) {
context.setGlobalCompositeOperation(Composite.COPY);
context.drawImage(imageElement, 0, 0);
context.setGlobalCompositeOperation(getComposite());
}
}
}
public boolean canUseImageElement() {
return canvas == null && imageElement != null;
}
public ImageElement getImageElement() {
return imageElement;
}
/**
* Sets the color for the following drawing operations
*
* @param color the color, encoded as RGBA8888
*/
public void setColor(int color) {
ensureCanvasExists();
r = (color >>> 24) & 0xff;
g = (color >>> 16) & 0xff;
b = (color >>> 8) & 0xff;
a = (color & 0xff) / 255f;
this.color = make(r, g, b, a);
context.setFillStyle(this.color);
context.setStrokeStyle(this.color);
}
/**
* Sets the color for the following drawing operations.
*
* @param r The red component.
* @param g The green component.
* @param b The blue component.
* @param a The alpha component.
*/
public void setColor(float r, float g, float b, float a) {
ensureCanvasExists();
this.r = (int) (r * 255);
this.g = (int) (g * 255);
this.b = (int) (b * 255);
this.a = a;
color = make(this.r, this.g, this.b, this.a);
context.setFillStyle(color);
context.setStrokeStyle(this.color);
}
/**
* Sets the color for the following drawing operations.
*
* @param color The color.
*/
public void setColor(Color color) {
setColor(color.r, color.g, color.b, color.a);
}
/**
* Fills the complete bitmap with the currently set color.
*/
public void fill() {
ensureCanvasExists();
context.clearRect(0, 0, getWidth(), getHeight());
rectangle(0, 0, getWidth(), getHeight(), DrawType.FILL);
}
// /**
// * Sets the width in pixels of strokes.
// *
// * @param width The stroke width in pixels.
// */
// public void setStrokeWidth (int width);
/**
* Draws a line between the given coordinates using the currently set color.
*
* @param x The x-coodinate of the first point
* @param y The y-coordinate of the first point
* @param x2 The x-coordinate of the first point
* @param y2 The y-coordinate of the first point
*/
public void drawLine(int x, int y, int x2, int y2) {
line(x, y, x2, y2, DrawType.STROKE);
}
/**
* Draws a rectangle outline starting at x, y extending by width to the right and by height downwards (y-axis points downwards)
* using the current color.
*
* @param x The x coordinate
* @param y The y coordinate
* @param width The width in pixels
* @param height The height in pixels
*/
public void drawRectangle(int x, int y, int width, int height) {
rectangle(x, y, width, height, DrawType.STROKE);
}
/**
* Draws an area form another Pixmap to this Pixmap.
*
* @param pixmap The other Pixmap
* @param x The target x-coordinate (top left corner)
* @param y The target y-coordinate (top left corner)
*/
public void drawPixmap(Pixmap pixmap, int x, int y) {
CanvasElement image = pixmap.getCanvasElement();
image(image, 0, 0, image.getWidth(), image.getHeight(), x, y, image.getWidth(), image.getHeight());
}
/**
* Draws an area form another Pixmap to this Pixmap.
*
* @param pixmap The other Pixmap
* @param x The target x-coordinate (top left corner)
* @param y The target y-coordinate (top left corner)
* @param srcx The source x-coordinate (top left corner)
* @param srcy The source y-coordinate (top left corner);
* @param srcWidth The width of the area form the other Pixmap in pixels
* @param srcHeight The height of the area form the other Pixmap in pixles
*/
public void drawPixmap(Pixmap pixmap, int x, int y, int srcx, int srcy, int srcWidth, int srcHeight) {
CanvasElement image = pixmap.getCanvasElement();
image(image, srcx, srcy, srcWidth, srcHeight, x, y, srcWidth, srcHeight);
}
/**
* Draws an area form another Pixmap to this Pixmap. This will automatically scale and stretch the source image to the
* specified target rectangle. Use {@link Pixmap#setFilter(Filter)} to specify the type of filtering to be used (nearest
* neighbour or bilinear).
*
* @param pixmap The other Pixmap
* @param srcx The source x-coordinate (top left corner)
* @param srcy The source y-coordinate (top left corner);
* @param srcWidth The width of the area form the other Pixmap in pixels
* @param srcHeight The height of the area form the other Pixmap in pixles
* @param dstx The target x-coordinate (top left corner)
* @param dsty The target y-coordinate (top left corner)
* @param dstWidth The target width
* @param dstHeight the target height
*/
public void drawPixmap(Pixmap pixmap, int srcx, int srcy, int srcWidth, int srcHeight, int dstx, int dsty, int dstWidth,
int dstHeight) {
image(pixmap.getCanvasElement(), srcx, srcy, srcWidth, srcHeight, dstx, dsty, dstWidth, dstHeight);
}
/**
* Fills a rectangle starting at x, y extending by width to the right and by height downwards (y-axis points downwards) using
* the current color.
*
* @param x The x coordinate
* @param y The y coordinate
* @param width The width in pixels
* @param height The height in pixels
*/
public void fillRectangle(int x, int y, int width, int height) {
rectangle(x, y, width, height, DrawType.FILL);
}
/**
* Draws a circle outline with the center at x,y and a radius using the current color and stroke width.
*
* @param x The x-coordinate of the center
* @param y The y-coordinate of the center
* @param radius The radius in pixels
*/
public void drawCircle(int x, int y, int radius) {
circle(x, y, radius, DrawType.STROKE);
}
/**
* Fills a circle with the center at x,y and a radius using the current color.
*
* @param x The x-coordinate of the center
* @param y The y-coordinate of the center
* @param radius The radius in pixels
*/
public void fillCircle(int x, int y, int radius) {
circle(x, y, radius, DrawType.FILL);
}
/**
* Fills a triangle with vertices at x1,y1 and x2,y2 and x3,y3 using the current color.
*
* @param x1 The x-coordinate of vertex 1
* @param y1 The y-coordinate of vertex 1
* @param x2 The x-coordinate of vertex 2
* @param y2 The y-coordinate of vertex 2
* @param x3 The x-coordinate of vertex 3
* @param y3 The y-coordinate of vertex 3
*/
public void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
triangle(x1, y1, x2, y2, x3, y3, DrawType.FILL);
}
/**
* Returns the 32-bit RGBA8888 value of the pixel at x, y. For Alpha formats the RGB components will be one.
*
* @param x The x-coordinate
* @param y The y-coordinate
* @return The pixel color in RGBA8888 format.
*/
public int getPixel(int x, int y) {
ensureCanvasExists();
if (pixels == null) pixels = context.getImageData(0, 0, width, height).getData();
int i = x * 4 + y * width * 4;
int r = pixels.get(i + 0) & 0xff;
int g = pixels.get(i + 1) & 0xff;
int b = pixels.get(i + 2) & 0xff;
int a = pixels.get(i + 3) & 0xff;
return (r << 24) | (g << 16) | (b << 8) | (a);
}
/**
* Draws a pixel at the given location with the current color.
*
* @param x the x-coordinate
* @param y the y-coordinate
*/
public void drawPixel(int x, int y) {
rectangle(x, y, 1, 1, DrawType.FILL);
}
/**
* Draws a pixel at the given location with the given color.
*
* @param x the x-coordinate
* @param y the y-coordinate
* @param color the color in RGBA8888 format.
*/
public void drawPixel(int x, int y, int color) {
setColor(color);
drawPixel(x, y);
}
private void circle(int x, int y, int radius, DrawType drawType) {
ensureCanvasExists();
if (blending == Blending.None) {
context.setFillStyle(clearColor);
context.setStrokeStyle(clearColor);
context.setGlobalCompositeOperation("destination-out");
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
fillOrStrokePath(drawType);
context.closePath();
context.setFillStyle(color);
context.setStrokeStyle(color);
context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
}
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
fillOrStrokePath(drawType);
context.closePath();
pixels = null;
}
private void line(int x, int y, int x2, int y2, DrawType drawType) {
ensureCanvasExists();
if (blending == Blending.None) {
context.setFillStyle(clearColor);
context.setStrokeStyle(clearColor);
context.setGlobalCompositeOperation("destination-out");
context.beginPath();
context.moveTo(x, y);
context.lineTo(x2, y2);
fillOrStrokePath(drawType);
context.closePath();
context.setFillStyle(color);
context.setStrokeStyle(color);
context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
}
context.beginPath();
context.moveTo(x, y);
context.lineTo(x2, y2);
fillOrStrokePath(drawType);
context.closePath();
pixels = null;
}
private void rectangle(int x, int y, int width, int height, DrawType drawType) {
ensureCanvasExists();
if (blending == Blending.None) {
context.setFillStyle(clearColor);
context.setStrokeStyle(clearColor);
context.setGlobalCompositeOperation("destination-out");
context.beginPath();
context.rect(x, y, width, height);
fillOrStrokePath(drawType);
context.closePath();
context.setFillStyle(color);
context.setStrokeStyle(color);
context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
}
context.beginPath();
context.rect(x, y, width, height);
fillOrStrokePath(drawType);
context.closePath();
pixels = null;
}
private void triangle(int x1, int y1, int x2, int y2, int x3, int y3, DrawType drawType) {
ensureCanvasExists();
if (blending == Blending.None) {
context.setFillStyle(clearColor);
context.setStrokeStyle(clearColor);
context.setGlobalCompositeOperation("destination-out");
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x3, y3);
context.lineTo(x1, y1);
fillOrStrokePath(drawType);
context.closePath();
context.setFillStyle(color);
context.setStrokeStyle(color);
context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
}
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x3, y3);
context.lineTo(x1, y1);
fillOrStrokePath(drawType);
context.closePath();
pixels = null;
}
private void image(CanvasElement image, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight) {
ensureCanvasExists();
if (blending == Blending.None) {
context.setFillStyle(clearColor);
context.setStrokeStyle(clearColor);
context.setGlobalCompositeOperation("destination-out");
context.beginPath();
context.rect(dstX, dstY, dstWidth, dstHeight);
fillOrStrokePath(DrawType.FILL);
context.closePath();
context.setFillStyle(color);
context.setStrokeStyle(color);
context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
}
context.drawImage(image, srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight);
pixels = null;
}
private void fillOrStrokePath(DrawType drawType) {
ensureCanvasExists();
switch (drawType) {
case FILL:
context.fill();
break;
case STROKE:
context.stroke();
break;
}
}
private enum DrawType {
FILL, STROKE
}
}