Skip to content

Commit

Permalink
Fixed problems with premultiplied alpha on GWT and added test to reve…
Browse files Browse the repository at this point in the history
…al problem in GWT Pixmap (#4234)
  • Loading branch information
barkholt authored and Tom-Ski committed Nov 6, 2016
1 parent 9ebcfd8 commit 8902da2
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 19 deletions.
Expand Up @@ -890,7 +890,13 @@ public void glTexImage2D (int target, int level, int internalformat, int width,
gl.texImage2D(target, level, internalformat, width, height, border, format, type, buffer);
} else {
Pixmap pixmap = Pixmap.pixmaps.get(((IntBuffer)pixels).get(0));
gl.texImage2D(target, level, internalformat, format, type, pixmap.getCanvasElement());
// Prefer to use the HTMLImageElement when possible, since reading from the CanvasElement can be lossy.
if (pixmap.canUseImageElement()) {
gl.texImage2D(target, level, internalformat, format, type, pixmap.getImageElement());
}
else {
gl.texImage2D(target, level, internalformat, format, type, pixmap.getCanvasElement());
}
}
}
}
Expand Down
Expand Up @@ -21,15 +21,18 @@
import java.util.HashMap;
import java.util.Map;

import com.badlogic.gdx.backends.gwt.GwtApplication;
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.aria.client.ImgRole;
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.canvas.dom.client.ImageData;
import com.google.gwt.dom.client.CanvasElement;
import com.google.gwt.dom.client.ImageElement;

Expand Down Expand Up @@ -92,18 +95,15 @@ public enum Filter {
static String clearColor = make(255, 255, 255, 1.0f);
static Blending blending;
CanvasPixelArray pixels;
private ImageElement imageElement;

public Pixmap (FileHandle file) {
GwtFileHandle gwtFile = (GwtFileHandle)file;
ImageElement img = gwtFile.preloader.images.get(file.path());
if (img == null) throw new GdxRuntimeException("Couldn't load image '" + file.path() + "', file does not exist");
create(img.getWidth(), img.getHeight(), Format.RGBA8888);
context.setGlobalCompositeOperation(Composite.COPY);
context.drawImage(img, 0, 0);
context.setGlobalCompositeOperation(getComposite());
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;
}

Expand All @@ -112,27 +112,30 @@ private static Composite getComposite () {
}

public Pixmap (ImageElement img) {
create(img.getWidth(), img.getHeight(), Format.RGBA8888);
context.drawImage(img, 0, 0);
this(-1, -1, img);
}

public Pixmap (int width, int height, Format format) {
create(width, height, 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 (int width, int height, Format format2) {
this.width = width;
this.height = height;
this.format = Format.RGBA8888;
private void create () {
canvas = Canvas.createIfSupported();
canvas.getCanvasElement().setWidth(width);
canvas.getCanvasElement().setHeight(height);
context = canvas.getContext2d();
context.setGlobalCompositeOperation(getComposite());
buffer = BufferUtils.newIntBuffer(1);
id = nextId++;
buffer.put(0, id);
pixmaps.put(id, this);
}

public static String make (int r2, int g2, int b2, float a2) {
Expand All @@ -145,6 +148,7 @@ public static void setBlending (Blending blending) {
Pixmap.blending = blending;
Composite composite = getComposite();
for (Pixmap pixmap : pixmaps.values()) {
pixmap.ensureCanvasExists();
pixmap.context.setGlobalCompositeOperation(composite);
}
}
Expand Down Expand Up @@ -194,12 +198,33 @@ public void dispose () {
}

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;
Expand All @@ -216,6 +241,7 @@ public void setColor (int color) {
* @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);
Expand All @@ -233,6 +259,7 @@ public void setColor (Color color) {

/** 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);
}
Expand Down Expand Up @@ -354,6 +381,7 @@ public void fillTriangle (int x1, int y1, int x2, int y2, int x3, int y3) {
* @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;
Expand Down Expand Up @@ -382,6 +410,7 @@ public void drawPixel (int x, int y, int color) {
}

private void circle (int x, int y, int radius, DrawType drawType) {
ensureCanvasExists();
if (blending == Blending.None) {
context.setFillStyle(clearColor);
context.setStrokeStyle(clearColor);
Expand All @@ -402,6 +431,7 @@ private void circle (int x, int y, int radius, DrawType drawType) {
}

private void line(int x, int y, int x2, int y2, DrawType drawType) {
ensureCanvasExists();
if (blending == Blending.None) {
context.setFillStyle(clearColor);
context.setStrokeStyle(clearColor);
Expand All @@ -424,6 +454,7 @@ private void line(int x, int y, int x2, int y2, DrawType drawType) {
}

private void rectangle(int x, int y, int width, int height, DrawType drawType) {
ensureCanvasExists();
if (blending == Blending.None) {
context.setFillStyle(clearColor);
context.setStrokeStyle(clearColor);
Expand All @@ -444,6 +475,7 @@ private void rectangle(int x, int y, int width, int height, DrawType drawType) {
}

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);
Expand All @@ -470,6 +502,7 @@ private void triangle(int x1, int y1, int x2, int y2, int x3, int y3, DrawType d
}

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);
Expand All @@ -487,6 +520,7 @@ private void image (CanvasElement image, int srcX, int srcY, int srcWidth, int s
}

private void fillOrStrokePath(DrawType drawType) {
ensureCanvasExists();
switch (drawType) {
case FILL:
context.fill();
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,74 @@
/*******************************************************************************
* 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.tests;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.FileTextureData;
import com.badlogic.gdx.tests.utils.GdxTest;

/**
* This tests is used to illustrate how backing the Pixmap in GWT with a Canvas can cause problems.
* @author barkholt
*
*/
public class GWTLossyPremultipliedAlphaTest extends GdxTest {
private SpriteBatch batch;
private Texture goodTexture;
private Texture badTexture;

public void create () {
batch = new SpriteBatch();
// Load a texture with premultiplied alpha
FileTextureData data = new FileTextureData(Gdx.files.internal("data/premultiplied_alpha_test.png"), null, null, false);
goodTexture = new Texture(data);

// Load the texture again. But this time, force the GWT implementation of Pixmap to move to a Canvas representation of the image
Pixmap pixmap = new Pixmap(Gdx.files.internal("data/premultiplied_alpha_test.png"));
pixmap.getPixel(0, 0);
FileTextureData data1 = new FileTextureData(null, pixmap, null, false);
badTexture = new Texture(data1);
}

public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

batch.begin();
batch.setBlendFunction(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA);
batch.draw(badTexture,
0,
Gdx.graphics.getHeight(),
Gdx.graphics.getWidth() * 0.5f,
-Gdx.graphics.getHeight());

batch.draw(goodTexture,
Gdx.graphics.getWidth() * 0.5f,
Gdx.graphics.getHeight(),
Gdx.graphics.getWidth() * 0.5f,
-Gdx.graphics.getHeight());

batch.end();
}

public boolean needsGL20 () {
return false;
}
}
Expand Up @@ -63,6 +63,7 @@
import com.badlogic.gdx.tests.FrameBufferTest;
import com.badlogic.gdx.tests.FramebufferToTextureTest;
import com.badlogic.gdx.tests.GLProfilerErrorTest;
import com.badlogic.gdx.tests.GWTLossyPremultipliedAlphaTest;
import com.badlogic.gdx.tests.GestureDetectorTest;
import com.badlogic.gdx.tests.GroupCullingTest;
import com.badlogic.gdx.tests.GroupFadeTest;
Expand Down Expand Up @@ -759,5 +760,9 @@ public GdxTest instance () {
public GdxTest instance () {
return new TimeUtilsTest();
}
}, new Instancer() {
public GdxTest instance() {
return new GWTLossyPremultipliedAlphaTest();
}
}};
}

0 comments on commit 8902da2

Please sign in to comment.