/* 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.control; import java.awt.BasicStroke; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.CropImageFilter; import java.awt.image.FilteredImageSource; import java.awt.image.ImageObserver; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import javax.imageio.ImageIO; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.lobobrowser.html.domimpl.HTMLCanvasElementImpl; import org.lobobrowser.html.info.CanvasInfo; import org.lobobrowser.http.UserAgentContext; import org.lobobrowser.w3c.html.HTMLCanvasElement; /** * The Class CanvasControl. */ public class CanvasControl extends BaseControl { /** The Constant serialVersionUID. */ private static final long serialVersionUID = 1L; /** The Constant logger. */ private static final Logger logger = LogManager.getLogger(CanvasControl.class.getName()); /** The width. */ private int width; /** The height. */ private int height; /** The list canvas info. */ private ArrayList<CanvasInfo> listCanvasInfo; /** * Instantiates a new canvas control. * * @param modelNode * the model node */ public CanvasControl(HTMLCanvasElementImpl modelNode) { super(modelNode); width = modelNode.getWidth(); height = modelNode.getHeight(); listCanvasInfo = modelNode.getListCanvasInfo(); } /* * (non-Javadoc) * * @see javax.swing.JComponent#paint(java.awt.Graphics) */ @Override public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D) g; g2d.drawRect(0, 0, new Integer(width), new Integer(height)); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); for (int i = 0; i < listCanvasInfo.size(); i++) { CanvasInfo ci = listCanvasInfo.get(i); switch (ci.getMethod()) { case HTMLCanvasElement.FILL: fill(g2d, ci); break; case HTMLCanvasElement.FILL_RECT: fillRect(g2d, ci); break; case HTMLCanvasElement.FILL_TEXT: fillText(g2d, ci); break; case HTMLCanvasElement.STROKE: stroke(g2d, ci); break; case HTMLCanvasElement.STROKE_RECT: strokeRect(g2d, ci); break; case HTMLCanvasElement.STROKE_TEXT: strokeText(g2d, ci); break; case HTMLCanvasElement.IMAGE: image(g2d, ci); break; case HTMLCanvasElement.IMAGE_CLIP: imageClip(g2d, ci); break; case HTMLCanvasElement.CLEAR_RECT: clearRect(g2d, ci); break; default: break; } } } /** * Fill. * * @param g * the g * @param ci * the ci */ private void fill(Graphics2D g, CanvasInfo ci) { g.setTransform(new AffineTransform()); g.setPaint(ci.getFillPaint()); g.setTransform(g.getTransform()); g.fill(ci.getPath()); } /** * Fill rect. * * @param g * the g * @param ci * the ci */ private void fillRect(Graphics2D g, CanvasInfo ci) { g.setComposite(ci.getGlobalAlpha()); g.setPaint(ci.getFillPaint()); g.rotate(ci.getRotate()); g.scale(ci.getScaleX(), ci.getScaleY()); g.translate(ci.getTranslateX(), ci.getTranslateY()); g.transform(ci.getAffineTransform()); g.fillRect(ci.getX(), ci.getY(), ci.getWidth(), ci.getHeight()); } /** * Fill text. * * @param g * the g * @param ci * the ci */ private void fillText(Graphics2D g, CanvasInfo ci) { g.setPaint(ci.getFillPaint()); g.setFont(ci.getFont()); g.rotate(ci.getRotate()); g.scale(ci.getScaleX(), ci.getScaleY()); g.translate(ci.getTranslateX(), ci.getTranslateY()); Point2D.Float f = calcTextPos(ci, g); g.drawString(ci.getText(), f.x, f.y); } /** * Stroke. * * @param g * the g * @param ci * the ci */ /** * Stroke. * * @param g * the g */ private void stroke(Graphics2D g, CanvasInfo ci) { int intLineCap = BasicStroke.CAP_BUTT; int intlineJoin = BasicStroke.JOIN_BEVEL; if ("round".equals(ci.getLineCap())) { intLineCap = BasicStroke.CAP_ROUND; } else if ("square".equals(ci.getLineCap())) { intLineCap = BasicStroke.CAP_SQUARE; } if ("round".equals(ci.getLineJoin())) { intlineJoin = BasicStroke.JOIN_ROUND; } else if ("miter".equals(ci.getLineJoin())) { intlineJoin = BasicStroke.JOIN_MITER; } g.setStroke(new BasicStroke(ci.getLineWidth(), intLineCap, intlineJoin, ci.getMiterLimit())); g.setPaint(ci.getStrokePaint()); g.draw(ci.getPath()); } /** * Stroke rect. * * @param g * the g * @param ci * the ci */ private void strokeRect(Graphics2D g, CanvasInfo ci) { g.setComposite(ci.getGlobalAlpha()); g.setPaint(ci.getStrokePaint()); g.rotate(ci.getRotate()); g.setStroke(new BasicStroke(ci.getLineWidth())); g.scale(ci.getScaleX(), ci.getScaleY()); g.translate(ci.getTranslateX(), ci.getTranslateY()); g.transform(ci.getAffineTransform()); g.drawRect(ci.getX(), ci.getY(), ci.getWidth(), ci.getHeight()); } /** * Stroke text. * * @param g * the g * @param ci * the ci */ private void strokeText(Graphics2D g, CanvasInfo ci) { FontRenderContext frc = new FontRenderContext(null, false, false); TextLayout tl = new TextLayout(ci.getText(), ci.getFont(), frc); Point2D.Float pos = calcTextPos(ci,g); AffineTransform textAt = AffineTransform.getTranslateInstance(pos.x, pos.y); textAt.translate(ci.getX(), ci.getY()); Shape outline = tl.getOutline(textAt); g.setPaint(ci.getStrokePaint()); g.rotate(ci.getRotate()); g.setStroke(new BasicStroke(2)); g.scale(ci.getScaleX(), ci.getScaleY()); g.translate(ci.getTranslateX(), ci.getTranslateY()); g.draw(outline); } /** * Image. * * @param g * the g * @param ci * the ci */ private void image(Graphics2D graphics, CanvasInfo ci) { URL u; try { u = new URL(ci.getImage().getSrc()); URLConnection con = u.openConnection(); con.setRequestProperty("User-Agent", UserAgentContext.DEFAULT_USER_AGENT); Image img = ImageIO.read(con.getInputStream()); AffineTransform at = new AffineTransform(ci.getWidth() / ci.getImage().getWidth(), 0, 0, ci.getHeight() / ci.getImage().getHeight(), ci.getX(), ci.getY()); graphics.drawImage(img, at, (ImageObserver) null); } catch (Exception e) { logger.error(e.getLocalizedMessage()); } } /** * Image clip. * * @param g * the g * @param ci * the ci */ private void imageClip(Graphics2D g, CanvasInfo ci) { URL u; try { u = new URL(ci.getImage().getSrc()); URLConnection con = u.openConnection(); con.setRequestProperty("User-Agent", UserAgentContext.DEFAULT_USER_AGENT); Image img = ImageIO.read(con.getInputStream()); img = createImage(new FilteredImageSource(img.getSource(), new CropImageFilter(ci.getSx(), ci.getSy(), ci.getSw(), ci.getSh()))); g.clip(new Rectangle2D.Float(ci.getDx(), ci.getDy(), ci.getDw(), ci.getDh())); g.drawImage(img, ci.getDx(), ci.getDy(), ci.getDw(), ci.getDh(), this); } catch (Exception e) { logger.error(e.getLocalizedMessage()); } } /** * Clear rect. * * @param g * the g * @param ci * the ci */ private void clearRect(Graphics2D g, CanvasInfo ci) { g.clearRect(ci.getX(), ci.getY(), ci.getWidth(), ci.getHeight()); } private Point2D.Float calcTextPos(CanvasInfo ci, Graphics2D graphics) { FontMetrics metrics = graphics.getFontMetrics(); int x = ci.getX(); int y = ci.getY(); String bs = ci.getBaseline(); String ta = ci.getTextAlign(); if ("center".equals(ta)) { x = x - metrics.stringWidth(ci.getText()) / 2; } else if ("right".equals(ta)) { x = x - metrics.stringWidth(ci.getText()); } if ("baseline".equals(bs)) { y = y - metrics.getLeading() + metrics.getAscent(); } else if ("top".equals(bs)) { y = y - metrics.getLeading(); } else if ("middle".equals(bs)) { y = y - metrics.getLeading() - metrics.getAscent() / 2; } else if ("bottom".equals(bs) || "text-bottom".equals(bs)) { y = y - metrics.getHeight(); } else { y = y + metrics.getLeading() + metrics.getAscent(); } return new Point2D.Float(x, y); } }