/*
GNU GENERAL LICENSE
Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
verion 3 of the License, or (at your option) any later version.
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
General License for more details.
You should have received a copy of the GNU General Public
along with this program. If not, see <http://www.gnu.org/licenses/>.
Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it
*/
package org.lobobrowser.html.domimpl;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.LinearGradientPaint;
import java.awt.Paint;
import java.awt.RadialGradientPaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import org.lobobrowser.html.HtmlAttributeProperties;
import org.lobobrowser.html.info.CanvasInfo;
import org.lobobrowser.html.style.CSSValuesProperties;
import org.lobobrowser.html.style.HtmlValues;
import org.lobobrowser.util.gui.ColorFactory;
import org.lobobrowser.util.gui.FontFactory;
import org.lobobrowser.util.gui.LAFSettings;
import org.lobobrowser.w3c.file.FileCallback;
import org.lobobrowser.w3c.html.CanvasGradient;
import org.lobobrowser.w3c.html.CanvasImageData;
import org.lobobrowser.w3c.html.CanvasPattern;
import org.lobobrowser.w3c.html.CanvasRenderingContext;
import org.lobobrowser.w3c.html.CanvasRenderingContext2D;
import org.lobobrowser.w3c.html.HTMLCanvasElement;
import org.lobobrowser.w3c.html.HTMLImageElement;
import org.lobobrowser.w3c.html.TextMetrics;
/**
* The Class HTMLCanvasElementImpl.
*/
public class HTMLCanvasElementImpl extends HTMLAbstractUIElement implements
HTMLCanvasElement, CanvasRenderingContext2D, CanvasPattern {
/** The Constant FONT_FACTORY. */
private static final FontFactory FONT_FACTORY = FontFactory.getInstance();
/** The list canvas info. */
private ArrayList<CanvasInfo> listCanvasInfo;
/** The fill style. */
private Object fillStyle;
/** The stroke style. */
private Object strokeStyle;
/** The fill paint. */
private Paint fillPaint;
/** The stroke paint. */
private Paint strokePaint;
/** The line width. */
private int lineWidth;
/** The global alpha. */
private Float globalAlpha;
/** The translate x. */
private int translateX;
/** The translate y. */
private int translateY;
/** The rotate. */
private Double rotate;
/** The scale x. */
private int scaleX;
/** The scale y. */
private int scaleY;
/** The font. */
private Font font;
/** The int line cap. */
private int intLineCap;
/** The intline join. */
private int intlineJoin;
/** The miter limit. */
private int miterLimit;
/** The path. */
private GeneralPath path;
/** The Affine Transform. */
private AffineTransform affineTransform;
/** The global Composite Operation. */
private String globalCompositeOperation;
/** The text align */
private String textAlign;
/** The baseline. */
private String baseline;
/**
* Instantiates a new HTML canvas element impl.
*
* @param name
* the name
*/
public HTMLCanvasElementImpl(String name) {
super(name);
listCanvasInfo = new ArrayList<CanvasInfo>();
lineWidth = 1;
globalAlpha = 1.0f;
translateX = 0;
translateY = 0;
rotate = 0.0;
scaleX = 1;
scaleY = 1;
miterLimit = 1;
intLineCap = BasicStroke.CAP_BUTT;
intlineJoin = BasicStroke.JOIN_BEVEL;
fillStyle = Color.BLACK;
strokeStyle = Color.BLACK;
fillPaint = Color.BLACK;
strokePaint = Color.BLACK;
affineTransform = new AffineTransform(1,0,0,1,0,0);
font = FONT_FACTORY.getFont(Font.SANS_SERIF, null, null, null, LAFSettings.getInstance().getFontSize(), null, null,0,false,0);
globalCompositeOperation = "source-over";
textAlign = "left";
baseline = "alphabetic";
path = new GeneralPath();
}
@Override
public int getWidth() {
String widthText = this.getAttribute(HtmlAttributeProperties.WIDTH);
return HtmlValues.getPixelSize(widthText, null, 1);
}
@Override
public void setWidth(int width) {
this.setAttribute(HtmlAttributeProperties.WIDTH, String.valueOf(width));
}
@Override
public int getHeight() {
String heightText = this.getAttribute(HtmlAttributeProperties.HEIGHT);
return HtmlValues.getPixelSize(heightText, null, 1);
}
@Override
public void setHeight(int height) {
this.setAttribute(HtmlAttributeProperties.HEIGHT, String.valueOf(height));
}
@Override
public String toDataURL() {
// TODO Auto-generated method stub
return null;
}
@Override
public String toDataURL(String type, Object... args) {
// TODO Auto-generated method stub
return null;
}
@Override
public CanvasRenderingContext getContext(String contextId) {
return this;
}
@Override
public HTMLCanvasElement getCanvas() {
return this;
}
@Override
public Object getFillStyle() {
return fillStyle;
}
@Override
public void setFillStyle(Object style) {
this.fillStyle = style;
if (style instanceof CanvasGradient) {
DOMCanvasGradientImpl cgi = (DOMCanvasGradientImpl) style;
fillPaint = gradient(cgi.getFractions(), cgi.getColors(),
cgi.getLinearX(), cgi.getLinearX1(),
cgi.getLinearY(), cgi.getLinearY1(),
cgi.getR1(),cgi.getR2());
} else if (style instanceof CanvasPattern) {
//fillPaint = ((DOMCanvasPatternImpl) style).getPaint();
} else if (style instanceof String) {
fillPaint = ColorFactory.getInstance().getColor(style.toString());
}
}
@Override
public Font getFont() {
return font;
}
@Override
public void setFont(String sfont) {
font = getFontValue(sfont);
}
@Override
public Double getGlobalAlpha() {
return globalAlpha.doubleValue();
}
@Override
public void setGlobalAlpha(Double ga) {
globalAlpha = ga.floatValue();
setGlobalCompositeOperation(globalCompositeOperation);
}
@Override
public String getGlobalCompositeOperation() {
return globalCompositeOperation;
}
@Override
public void setGlobalCompositeOperation(String op) {
globalCompositeOperation = op;
}
@Override
public String getLineCap() {
if (intLineCap == BasicStroke.CAP_ROUND) {
return "round";
} else if (intLineCap == BasicStroke.CAP_SQUARE) {
return "square";
} else {
return "";
}
}
@Override
public void setLineCap(String lineCap) {
if ("round".equals(lineCap)) {
intLineCap = BasicStroke.CAP_ROUND;
} else if ("square".equals(lineCap)) {
intLineCap = BasicStroke.CAP_SQUARE;
}
}
@Override
public String getLineJoin() {
if (intlineJoin == BasicStroke.CAP_ROUND) {
return "round";
} else if (intlineJoin == BasicStroke.JOIN_MITER) {
return "miter";
} else {
return "";
}
}
@Override
public void setLineJoin(String lineJoin) {
if ("round".equals(lineJoin)) {
intlineJoin = BasicStroke.JOIN_ROUND;
} else if ("miter".equals(lineJoin)) {
intlineJoin = BasicStroke.JOIN_MITER;
}
}
@Override
public int getLineWidth() {
return lineWidth;
}
@Override
public void setLineWidth(int lw) {
lineWidth = lw;
}
@Override
public int getMiterLimit() {
return miterLimit;
}
@Override
public void setMiterLimit(int ml) {
miterLimit = ml;
}
@Override
public int getShadowBlur() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setShadowBlur(int arg) {
// TODO Auto-generated method stub
}
@Override
public String getShadowColor() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setShadowColor(String arg) {
// TODO Auto-generated method stub
}
@Override
public int getShadowOffsetX() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setShadowOffsetX(int arg) {
// TODO Auto-generated method stub
}
@Override
public int getShadowOffsetY() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setShadowOffsetY(int arg) {
// TODO Auto-generated method stub
}
@Override
public Object getStrokeStyle() {
return strokeStyle;
}
@Override
public void setStrokeStyle(Object style) {
this.strokeStyle = style;
if (style instanceof CanvasGradient) {
DOMCanvasGradientImpl cgi = (DOMCanvasGradientImpl) style;
strokePaint = gradient(cgi.getFractions(), cgi.getColors(),
cgi.getLinearX(), cgi.getLinearX1(),
cgi.getLinearY(), cgi.getLinearY1(),
cgi.getR1(),cgi.getR2());
} else if (style instanceof CanvasPattern) {
//strokePaint = ((DOMCanvasPatternImpl) style).getPaint();
} else if (style instanceof String) {
strokePaint = ColorFactory.getInstance().getColor(style.toString());
}
}
@Override
public String getTextAlign() {
return textAlign;
}
@Override
public void setTextAlign(String textAlign) {
this.textAlign = textAlign;
}
@Override
public String getTextBaseline() {
return baseline;
}
@Override
public void setTextBaseline(String bs) {
baseline = bs;
}
@Override
public void arc(int x, int y, int radius, int startAngle, int endAngle) {
path.append(buildArc(x, y, radius, startAngle, endAngle, false), true);
}
@Override
public void arc(int x, int y, int radius, int startAngle, int endAngle, boolean anticlockwise) {
path.append(buildArc(x, y, radius, startAngle, endAngle, anticlockwise), true);
}
@Override
public void arcTo(int x, int y, int rx, int ry, int radius) {
// TODO Auto-generated method stub
}
@Override
public void beginPath() {
path = new GeneralPath();
}
@Override
public void bezierCurveTo(int cp1x, int cp1y, int cp2x, int cp2y, int x, int y) {
path.curveTo(cp1x, cp1y, cp2x, cp2y, x, y);
}
@Override
public void clearRect(int x, int y, int width, int height) {
CanvasInfo clearect = new CanvasInfo();
clearect.setX(x);
clearect.setY(y);
clearect.setWidth(width);
clearect.setHeight(height);
clearect.setMethod(CLEAR_RECT);
listCanvasInfo.add(clearect);
}
@Override
public void clearShadow() {
// TODO Auto-generated method stub
}
@Override
public void clip() {
// TODO Auto-generated method stub
}
@Override
public void closePath() {
path.closePath();
}
@Override
public CanvasGradient createLinearGradient(Object x0, Object y0, Object x1, Object y1) {
return new DOMCanvasGradientImpl(x0, y0, x1, y1);
}
@Override
public CanvasPattern createPattern(HTMLCanvasElement canvas, String repetitionType) {
// TODO Auto-generated method stub
return null;
}
@Override
public CanvasPattern createPattern(HTMLImageElement image, String repetitionType) {
// TODO Auto-generated method stub
return null;
}
@Override
public CanvasGradient createRadialGradient(Object x0, Object y0, Object r0, Object x1, Object y1, Object r1) {
return new DOMCanvasGradientImpl(x0, y0, x1, y1, r0, r1);
}
@Override
public void drawImage(Object image, Integer x, Integer y) {
CanvasInfo cimage = new CanvasInfo();
if (image instanceof HTMLImageElementImpl) {
cimage.setImage((HTMLImageElementImpl) image);
cimage.setX(x);
cimage.setY(y);
cimage.setWidth(getWidth());
cimage.setHeight(getHeight());
cimage.setMethod(IMAGE);
listCanvasInfo.add(cimage);
}
}
@Override
public void drawImage(Object image, Integer x, Integer y, Integer width, Integer height) {
CanvasInfo cimage = new CanvasInfo();
if (image instanceof HTMLImageElementImpl) {
cimage.setImage((HTMLImageElementImpl) image);
cimage.setX(x);
cimage.setY(y);
cimage.setWidth(width);
cimage.setHeight(height);
cimage.setMethod(IMAGE);
}
listCanvasInfo.add(cimage);
}
@Override
public void drawImage(Object image, Integer sx, Integer sy, Integer sw, Integer sh, Integer dx, Integer dy, Integer dw, Integer dh) {
CanvasInfo cimage = new CanvasInfo();
if (image instanceof HTMLImageElementImpl) {
cimage.setImage((HTMLImageElementImpl) image);
cimage.setSx(sx);
cimage.setSy(sy);
cimage.setSw(sw);
cimage.setSh(sh);
cimage.setDx(dx);
cimage.setDy(dy);
cimage.setDw(dw);
cimage.setDh(dh);
cimage.setMethod(IMAGE_CLIP);
}
listCanvasInfo.add(cimage);
}
@Override
public void fill() {
CanvasInfo fill = new CanvasInfo();
fill.setMethod(FILL);
fill.setPath(path);
fill.setFillPaint(fillPaint);
fill.setLineCap(getLineCap());
fill.setLineJoin(getLineJoin());
fill.setLineWidth(lineWidth > 1 ? lineWidth : lineWidth);
listCanvasInfo.add(fill);
}
@Override
public void fillRect(int x, int y, int width, int height) {
CanvasInfo fillrect = new CanvasInfo();
fillrect.setX(x);
fillrect.setY(y);
fillrect.setWidth(width);
fillrect.setHeight(height);
fillrect.setFillPaint(fillPaint);
fillrect.setGlobalAlpha(comosite(globalCompositeOperation));
fillrect.setTranslateX(translateX);
fillrect.setTranslateY(translateY);
fillrect.setRotate(rotate);
fillrect.setScaleX(scaleX);
fillrect.setScaleY(scaleY);
fillrect.setAffineTransform(affineTransform);
fillrect.setMethod(FILL_RECT);
listCanvasInfo.add(fillrect);
}
@Override
public void fillText(String text, int x, int y) {
fillText(text, x, y, 0);
}
@Override
public void fillText(String text, int x, int y, int maxWidth) {
CanvasInfo fillText = new CanvasInfo();
fillText.setText(text);
fillText.setX(x);
fillText.setY(y);
fillText.setFillPaint(fillPaint);
fillText.setFont(font);
fillText.setMethod(FILL_TEXT);
fillText.setTranslateX(translateX);
fillText.setTranslateY(translateY);
fillText.setRotate(rotate);
fillText.setScaleX(scaleX);
fillText.setScaleY(scaleY);
fillText.setMaxWidth(maxWidth);
fillText.setTextAlign(textAlign);
fillText.setBaseline(baseline);
listCanvasInfo.add(fillText);
}
@Override
public boolean isPointInPath(int x, int y) {
Point2D p = new Point2D.Float(x, y);
return path.contains(p);
}
@Override
public void lineTo(int x, int y) {
path.lineTo(x, y);
}
@Override
public TextMetrics measureText(String text) {
// TODO Auto-generated method stub
return null;
}
@Override
public void moveTo(int x, int y) {
path.moveTo(x, y);
}
@Override
public CanvasImageData getImageData(int sx, int sy, int sw, int sh) {
// TODO Auto-generated method stub
return null;
}
@Override
public void putImageData(CanvasImageData imagedata, int dx, int dy) {
// TODO Auto-generated method stub
}
@Override
public void putImageData(CanvasImageData imagedata, int dx, int dy, int dirtyX, int dirtyY, int dirtyWidth, int dirtyHeight) {
// TODO Auto-generated method stub
}
@Override
public void quadraticCurveTo(int cpx, int cpy, int x, int y) {
path.quadTo(cpx, cpy, x, y);
}
@Override
public void rect(int x, int y, int width, int height) {
path.append(new Rectangle2D.Double(x, y, width, height), true);
}
@Override
public void restore() {
// TODO Auto-generated method stub
}
@Override
public void rotate(double angle) {
rotate = angle;
}
@Override
public void save() {
// TODO Auto-generated method stub
}
@Override
public void scale(int sx, int sy) {
scaleX = sx;
scaleY = sy;
}
@Override
public void setTransform(Double m11, Double m12, Double m21, Double m22, Double dx, Double dy) {
transform(m11, m12, m21, m22, dx, dy);
}
@Override
public void transform(Double m11, Double m12, Double m21, Double m22, Double dx, Double dy) {
affineTransform = new AffineTransform(m11, m12, m21, m22, dx, dy);
}
@Override
public void stroke() {
CanvasInfo stroke = new CanvasInfo();
stroke.setMethod(STROKE);
stroke.setPath(path);
stroke.setStrokePaint(strokePaint);
stroke.setLineCap(getLineCap());
stroke.setLineJoin(getLineJoin());
stroke.setLineWidth(lineWidth > 1 ? lineWidth : lineWidth);
listCanvasInfo.add(stroke);
}
@Override
public void strokeRect(int x, int y, int width, int height) {
strokeRect(x, y, width, height, lineWidth);
}
@Override
public void strokeRect(int x, int y, int width, int height, int lineWidth) {
CanvasInfo strokeRect = new CanvasInfo();
strokeRect.setX(x);
strokeRect.setY(y);
strokeRect.setWidth(width);
strokeRect.setHeight(height);
strokeRect.setLineWidth(lineWidth > 1 ? lineWidth : this.lineWidth);
strokeRect.setStrokePaint(strokePaint);
strokeRect.setGlobalAlpha(comosite(globalCompositeOperation));
strokeRect.setTranslateX(translateX);
strokeRect.setTranslateY(translateY);
strokeRect.setRotate(rotate);
strokeRect.setScaleX(scaleX);
strokeRect.setScaleY(scaleY);
strokeRect.setAffineTransform(affineTransform);
strokeRect.setMethod(STROKE_RECT);
listCanvasInfo.add(strokeRect);
}
@Override
public void strokeText(String text, int x, int y) {
strokeText(text, x, y, 0);
}
@Override
public void strokeText(String text, int x, int y, int maxWidth) {
CanvasInfo strokeText = new CanvasInfo();
strokeText.setText(text);
strokeText.setX(x);
strokeText.setY(y);
strokeText.setStrokePaint(strokePaint);
strokeText.setFont(font);
strokeText.setMethod(STROKE_TEXT);
strokeText.setTranslateX(translateX);
strokeText.setTranslateY(translateY);
strokeText.setRotate(rotate);
strokeText.setScaleX(scaleX);
strokeText.setScaleY(scaleY);
strokeText.setMaxWidth(maxWidth);
strokeText.setTextAlign(textAlign);
strokeText.setBaseline(baseline);
listCanvasInfo.add(strokeText);
}
@Override
public void translate(int tx, int ty) {
translateX = tx;
translateY = ty;
}
/**
* Gets the font value.
*
* @param font
* the font
* @return the font value
*/
private Font getFontValue(String font) {
String[] arrFont = font.split(" ");
float fontSize = LAFSettings.getInstance().getFontSize();
String fontStyle = CSSValuesProperties.ITALIC;
String fontVariant = CSSValuesProperties.SMALL_CAPS;
String fontFamily = Font.SANS_SERIF;
String fontWeight = CSSValuesProperties.BOLD;
for (int i = 0; i < arrFont.length; i++) {
String prop = arrFont[i];
if (prop.contains("px") || prop.contains("pt")) {
fontSize = HtmlValues.getFontSize(prop, null);
}
if (prop.contains(CSSValuesProperties.NORMAL)
|| prop.contains(CSSValuesProperties.ITALIC)
|| prop.contains(CSSValuesProperties.OBLIQUE)) {
fontStyle = prop;
}
if (prop.contains(CSSValuesProperties.NORMAL)
|| prop.contains(CSSValuesProperties.SMALL_CAPS)) {
fontVariant = prop;
}
if (prop.contains(CSSValuesProperties.NORMAL)
|| prop.contains(CSSValuesProperties.BOLD)
|| prop.contains(CSSValuesProperties.BOLDER)
|| prop.contains(CSSValuesProperties.LIGHTER)) {
fontWeight = prop;
}
if (i == arrFont.length - 1) {
fontFamily = prop;
}
}
return FontFactory.getInstance().getFont(fontFamily, fontStyle, fontVariant, fontWeight, fontSize, null, null,0,false,0);
}
/**
* gradient.
*
* @return gradient paint
*/
private Paint gradient(ArrayList<Float> fractions,
ArrayList<Color> colors, Double linearX, Double linearX1,
Double linearY, Double linearY1, Double r1, Double r2) {
float[] floatArray = new float[fractions.size()];
int i = 0;
for (Float f : fractions) {
floatArray[i++] = (f != null ? f : Float.NaN);
}
Color[] colorArray = new Color[colors.size()];
int a = 0;
for (Color c : colors) {
colorArray[a++] = c;
}
Arrays.sort(floatArray);
if (r2 != null) {
return new RadialGradientPaint(linearX.floatValue(), linearY.floatValue(), r2.floatValue(),
linearX1.floatValue(), linearY1.floatValue(), floatArray, colorArray,
RadialGradientPaint.CycleMethod.NO_CYCLE);
} else {
return new LinearGradientPaint(linearX.floatValue(), linearX1.floatValue(), linearY.floatValue(),
linearY1.floatValue(), floatArray, colorArray);
}
}
/**
* Gets the list canvas info.
*
* @return the list canvas info
*/
public ArrayList<CanvasInfo> getListCanvasInfo() {
return listCanvasInfo;
}
/**
* Sets the list canvas info.
*
* @param listCanvasInfo
* the new list canvas info
*/
public void setListCanvasInfo(ArrayList<CanvasInfo> listCanvasInfo) {
this.listCanvasInfo = listCanvasInfo;
}
@Override
public void toBlob(FileCallback callback) {
// TODO Auto-generated method stub
}
@Override
public void toBlob(FileCallback callback, String type, Object... args) {
// TODO Auto-generated method stub
}
private AlphaComposite comosite(String op) {
int c;
if ("source-atop".equals(op)) {
c = AlphaComposite.SRC_ATOP;
} else if ("source-in".equals(op)) {
c = AlphaComposite.SRC_IN;
} else if ("source-out".equals(op)) {
c = AlphaComposite.SRC_OUT;
} else if ("destination-atop".equals(op)) {
c = AlphaComposite.DST_ATOP;
} else if ("destination-in".equals(op)) {
c = AlphaComposite.DST_IN;
} else if ("destination-out".equals(op)) {
c = AlphaComposite.DST_OUT;
} else if ("destination-over".equals(op)) {
c = AlphaComposite.DST_OVER;
} else if ("xor".equals(op)) {
c = AlphaComposite.XOR;
} else if ("over".equals(op)) {
c = AlphaComposite.CLEAR;
} else {
c = AlphaComposite.SRC_OVER;
}
return AlphaComposite.getInstance(c, globalAlpha);
}
private Arc2D.Double buildArc(int x, int y, int radius, int startAngle, int endAngle, boolean anticlockwise) {
boolean clockwise = !anticlockwise;
double twopi = 2 * Math.PI;
while (startAngle < 0) {
startAngle += twopi;
}
while (startAngle > twopi) {
startAngle -= twopi;
}
while (endAngle < 0) {
endAngle += twopi;
}
while (endAngle > twopi) {
endAngle -= twopi;
}
if (clockwise) {
if (startAngle > endAngle) {
endAngle += twopi;
}
} else {
if (startAngle < endAngle) {
endAngle -= twopi;
}
}
double ang = startAngle - endAngle;
if (ang == 0.0) {
ang = Math.PI * 2;
}
startAngle = -startAngle;
return new Arc2D.Double(x - radius, y - radius, 2 * radius, 2 * radius, Math.toDegrees(startAngle), Math.toDegrees(ang), Arc2D.OPEN);
}
}