/**
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2014 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.html5player.client;
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.CssColor;
import com.google.gwt.canvas.dom.client.ImageData;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.user.client.ui.Image;
public class Scene {
private static Scene instance = null;
private Canvas sceneCanvas;
private boolean isSceneCreated;
private CssColor fillColor;
private int sceneWidth = 0;
private int sceneHeight = 0;
//##########################################################################
private Scene() {
isSceneCreated = false;
}
/**
*
*/
public static Scene get() {
if(instance == null) {
instance = new Scene();
}
return instance;
}
//##########################################################################
/**
* Create a canvas
* @return true if scene is created or was already created, false if canvas
* is not supported
*/
public boolean createScene() {
if(!isSceneCreated) {
sceneCanvas = Canvas.createIfSupported();
if(sceneCanvas == null)
return false;
isSceneCreated = true;
fillColor = CssColor.make("rgb(255,255,255)");
}
CatrobatDebug.info("Scene created");
return true;
}
/**
* Create a canvas with the given measures
* @param sceneWidth
* @param sceneHeight
* @return true if scene is created or was already created, false if canvas
* is not supported
*/
public boolean createScene(int sceneWidth, int sceneHeight) {
if(createScene()) {
setSceneMeasures(sceneWidth, sceneHeight);
CatrobatDebug.info("Scene created with width: " + sceneWidth + " and height: " + sceneHeight);
return true;
}
return false;
}
/**
* Sets the given measures for front and back canvas
* @param sceneWidth
* @param sceneHeight
*/
public void setSceneMeasures(int sceneWidth, int sceneHeight) {
if(isSceneCreated) {
clearCanvas();
this.sceneWidth = sceneWidth;
this.sceneHeight = sceneHeight;
sceneCanvas.setWidth(sceneWidth + "px");
sceneCanvas.setHeight(sceneHeight + "px");
sceneCanvas.setCoordinateSpaceWidth(this.sceneWidth);
sceneCanvas.setCoordinateSpaceHeight(this.sceneHeight);
// back.getContext2d().translate(sceneWidth/2, sceneHeight/2);
CatrobatDebug.debug("Scene got measures - width: " + this.sceneWidth + " and height: " + this.sceneHeight);
}
}
public void zoomScene(double zoomValue){
this.sceneWidth = (int) (this.sceneWidth * zoomValue);
this.sceneHeight = (int) (this.sceneHeight * zoomValue);
sceneCanvas.setWidth(this.sceneWidth + "px");
sceneCanvas.setHeight(this.sceneHeight + "px");
}
//############################ DRAW IMAGE ##################################
/**
*
* @param image
* @param x
* @param y
* @param width
* @param height
*/
public void drawImage(Image image, double x, double y, double width, double height, double alpha) {
ImageElement imageElement = (ImageElement)image.getElement().cast();
drawImageElement(imageElement, x, y, width, height, alpha);
}
/**
*
* @param imageElement
* @param x
* @param y
* @param width
* @param height
*/
public void drawImageElement(ImageElement imageElement, double x, double y, double width, double height, double alpha) {
long start = System.currentTimeMillis();
Context2d context = sceneCanvas.getContext2d();
context.save();
context.setGlobalAlpha(alpha);
context.drawImage(imageElement, x, y, width, height);
context.restore();
CatrobatDebug.debug("drawImageElement-execution took " + (System.currentTimeMillis() - start) + " ms");
}
/**
*
* @param image
* @param translateX
* @param translateY
* @param x
* @param y
* @param width
* @param height
* @param degrees
* @param xSize
* @param ySize
*/
public void drawImage(Image image, double translateX, double translateY, double x, double y, double width, double height, double degrees, double xSize, double ySize, double alpha) {
long start = System.currentTimeMillis();
ImageElement imageElement = (ImageElement)image.getElement().cast();
drawImageElement(imageElement, translateX, translateY, x, y, width, height, degrees, xSize, ySize, alpha);
CatrobatDebug.debug("drawImage-execution took " + (System.currentTimeMillis() - start) + " ms");
}
/**
*
* @param imageElement
* @param translateX
* @param translateY
* @param x
* @param y
* @param width
* @param height
* @param degrees
* @param xSize
* @param ySize
*/
public void drawImageElement(ImageElement imageElement, double translateX, double translateY, double x, double y, double width, double height, double degrees, double xSize, double ySize, double alpha) {
long start = System.currentTimeMillis();
Context2d context = sceneCanvas.getContext2d();
context.save();
context.setGlobalAlpha(alpha);
context.translate(translateX, translateY);
context.rotate(Math.toRadians(degrees));
context.scale(xSize, ySize);
context.drawImage(imageElement, x, y, width, height);
//for testing - draws a rectangular around the sprite
// context.strokeRect(x, y, width, height);
//
context.restore();
CatrobatDebug.debug("drawImageElement-execution took " + (System.currentTimeMillis() - start) + " ms");
}
//############################### TEXT #####################################
/**
*
* @param fontSetting
*/
public void setFont(String fontSetting) {
sceneCanvas.getContext2d().setFont(fontSetting);
}
/**
*
* @param text
* @param x
* @param y
*/
public void write(String text, double x, double y) {
Context2d context = sceneCanvas.getContext2d();
context.fillText(text, x, y);
}
/**
*
* @param text
* @param x
* @param y
* @param align
*/
public void write(String text, double x, double y, String align) {
Context2d context = sceneCanvas.getContext2d();
context.setTextAlign(align);
context.fillText(text, x, y);
}
//############################### BRIGHTNESS ###############################
/**
*
* @param image
* @param translateX
* @param translateY
* @param x
* @param y
* @param width
* @param height
* @param degrees
* @param xSize
* @param ySize
*/
public void drawImageBrightness(Image image, double translateX, double translateY, double x, double y, double width, double height, double degrees, double alpha, double brightness) throws JavaScriptException {
ImageElement imageElement = (ImageElement)image.getElement().cast();
try {
drawImageElementBrightness(imageElement, translateX, translateY, x, y, width, height, degrees, alpha, brightness);
}
catch(JavaScriptException exception) {
throw exception;
}
}
/**
*
* @param imageElement
* @param translateX
* @param translateY
* @param x
* @param y
* @param width
* @param height
* @param degrees
* @param xSize
* @param ySize
*/
public void drawImageElementBrightness(ImageElement imageElement, double translateX, double translateY, double x, double y, double width, double height, double degrees, double alpha, double brightness) throws JavaScriptException {
Context2d context = sceneCanvas.getContext2d();
context.save();
context.setGlobalAlpha(alpha);
context.translate(translateX, translateY);
context.rotate(degrees * Math.PI / 180);
try {
Canvas adjustedImage = adjustImageBrightness(imageElement, brightness);
context.drawImage(adjustedImage.getCanvasElement(), x, y, width, height);
context.restore();
}
catch(JavaScriptException exception) {
context.restore();
throw exception;
}
}
/**
*
* @param imageElement
* @param brightness
* @return Canvas canvas with the adjusted image
*/
private Canvas adjustImageBrightness(ImageElement imageElement, double brightness) {
int width = imageElement.getWidth();
int height = imageElement.getHeight();
Canvas temp = Canvas.createIfSupported();
temp.setCoordinateSpaceWidth(width);
temp.setCoordinateSpaceHeight(height);
Context2d context = temp.getContext2d();
context.drawImage(imageElement, 0, 0);
ImageData imageData = context.getImageData(0, 0, width, height);
CanvasPixelArray pixelsData = imageData.getData();
int index = 0;
System.out.println(pixelsData.getLength());
int brightnessAdj = (int)(255d * brightness) - 255;
if(brightnessAdj != 0){
while(index < pixelsData.getLength()){
if((index +1) % 4 != 0){
int r = checkColorRange(pixelsData.get(index) + brightnessAdj); //red channel
pixelsData.set(index, r);
int g = checkColorRange(pixelsData.get(++index) + brightnessAdj); //green channel
pixelsData.set(index, g);
int b = checkColorRange(pixelsData.get(++index) + brightnessAdj); //blue channel
pixelsData.set(index, b);
index++; //alpha channel
}
index++;
}
}
context.putImageData(imageData, 0, 0);
return temp;
}
/**
* Checks if the given color value is within the range 0...255
* @param colorValue
* @return 0 if the value is lesser or equal than 0 <br />
* 255 if the value is greater or equal than 255 <br />
* otherwise the value itself
*/
private int checkColorRange(int colorValue) {
if(colorValue <= 0)
return 0;
else if(colorValue >= 255) {
return 255;
}
else {
return colorValue;
}
}
//############################### JSNI #####################################
/**
*
* @param image
* @param translateX
* @param translateY
* @param x
* @param y
* @param width
* @param height
* @param degrees
* @param xSize
* @param ySize
* @param alpha
*/
public void drawImageJSNI(Image image, double translateX, double translateY, double x, double y, double width, double height, double degrees, double alpha) {
ImageElement imageElement = (ImageElement)image.getElement().cast();
Context2d context = sceneCanvas.getContext2d();
this.drawImageJSNI(imageElement, translateX, translateY, x, y, width, height, degrees, alpha, context);
}
/**
*
* @param image
* @param translateX
* @param translateY
* @param x
* @param y
* @param width
* @param height
* @param degrees
* @param xSize
* @param ySize
* @param alpha
* @param context
*/
private native void drawImageJSNI(ImageElement image, double translateX, double translateY, double x, double y, double width, double height, double degrees, double alpha, Context2d context) /*-{
context.save();
context.globalAlpha = alpha;
context.translate(translateX, translateY);
context.rotate(degrees * Math.PI / 180);
context.drawImage(image, x, y, width, height);
context.restore();
}-*/;
//##########################################################################
/**
*
*/
public void clearCanvas() {
long start = System.currentTimeMillis();
Context2d context = sceneCanvas.getContext2d();
context.save();
context.setFillStyle(fillColor);
context.fillRect(0, 0, this.sceneWidth, this.sceneHeight);
context.restore();
CatrobatDebug.debug("clearCanvas-execution took " + (System.currentTimeMillis() - start) + " ms");
}
//##########################################################################
/**
*
* @return Canvas canvas
*/
public Canvas getCanvas() {
return sceneCanvas;
}
/**
*
*/
public boolean isSceneCreated() {
return isSceneCreated;
}
/**
*
*/
public int getSceneWidth() {
return this.sceneWidth;
}
/**
*
*/
public int getSceneHeight() {
return this.sceneHeight;
}
//##########################################################################
/**
* FOR UNIT-TESING
*/
public void reset() {
instance = null;
}
/**
* FOR TESING
*/
public void drawAxis() {
Context2d ctx = sceneCanvas.getContext2d();
ctx.beginPath();
ctx.moveTo(getSceneWidth()/2, 0);
ctx.lineTo(getSceneWidth()/2, getSceneHeight());
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo(0, getSceneHeight()/2);
ctx.lineTo(getSceneWidth(), getSceneHeight()/2);
ctx.stroke();
ctx.closePath();
}
}