/* * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.javafx.webkit.prism; import com.sun.javafx.geom.Arc2D; import com.sun.javafx.geom.Ellipse2D; import com.sun.javafx.geom.Path2D; import com.sun.javafx.geom.PathIterator; import com.sun.javafx.geom.Point2D; import com.sun.javafx.geom.RectBounds; import com.sun.javafx.geom.RoundRectangle2D; import com.sun.javafx.geom.transform.BaseTransform; import com.sun.webkit.graphics.WCPath; import com.sun.webkit.graphics.WCPathIterator; import com.sun.webkit.graphics.WCRectangle; import com.sun.javafx.logging.PlatformLogger; import com.sun.javafx.logging.PlatformLogger.Level; final class WCPathImpl extends WCPath { private final Path2D path; private boolean hasCP = false; private final static PlatformLogger log = PlatformLogger.getLogger(WCPathImpl.class.getName()); WCPathImpl() { if (log.isLoggable(Level.FINE)) { log.fine("Create empty WCPathImpl({0})", getID()); } path = new Path2D(); } WCPathImpl(WCPathImpl wcp) { if (log.isLoggable(Level.FINE)) { log.fine("Create WCPathImpl({0}) from WCPathImpl({1})", new Object[] { getID(), wcp.getID()}); } path = new Path2D(wcp.path); hasCP = wcp.hasCP; } public void addRect(double x, double y, double w, double h) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).addRect({1},{2},{3},{4})", new Object[] {getID(), x, y, w, h}); } hasCP = true; path.append(new RoundRectangle2D( (float)x, (float)y, (float)w, (int)h, 0.0f, 0.0f), false); } public void addEllipse(double x, double y, double w, double h) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).addEllipse({1},{2},{3},{4})", new Object[] {getID(), x, y, w, h}); } hasCP = true; path.append(new Ellipse2D((float)x, (float)y, (float)w, (float)h), false); } public void addArcTo(double x1, double y1, double x2, double y2, double r) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).addArcTo({1},{2},{3},{4})", new Object[] {getID(), x1, y1, x2, y2}); } Arc2D arc = new Arc2D(); arc.setArcByTangent( path.getCurrentPoint(), new Point2D((float) x1, (float) y1), new Point2D((float) x2, (float) y2), (float) r); hasCP = true; path.append(arc, true); } public void addArc(double x, double y, double r, double sa, double ea, boolean aclockwise) { // Use single precision float as native final float TWO_PI = 2.0f * (float) Math.PI; float startAngle = (float) sa; float endAngle = (float) ea; if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).addArc(x={1},y={2},r={3},sa=|{4}|,ea=|{5}|,aclock={6})", new Object[] {getID(), x, y, r, startAngle, endAngle, aclockwise}); } hasCP = true; float newEndAngle = endAngle; // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arc // If the anticlockwise argument is false and endAngle-startAngle is equal // to or greater than 2pi, or, // if the anticlockwise argument is true and startAngle-endAngle is equal to // or greater than 2pi, // then the arc is the whole circumference of this ellipse, and the point at // startAngle along this circle's circumference, measured in radians clockwise // from the ellipse's semi-major axis, acts as both the start point and the // end point. // below condition is already handled in normalizeAngles(), CanvasPath.cpp. // if (!anticlockwise && end_angle - start_angle >= twoPiFloat) { // new_end_angle = start_angle + twoPiFloat; // } else if (anticlockwise && start_angle - end_angle >= twoPiFloat) { // new_end_angle = start_angle - twoPiFloat; // } else // Otherwise, the arc is the path along the circumference of this ellipse // from the start point to the end point, going anti-clockwise if the // anticlockwise argument is true, and clockwise otherwise. // Since the points are on the ellipse, as opposed to being simply angles // from zero, the arc can never cover an angle greater than 2pi radians. // // NOTE: When startAngle = 0, endAngle = 2Pi and anticlockwise = true, the // spec does not indicate clearly. // We draw the entire circle, because some web sites use arc(x, y, r, 0, // 2*Math.PI, true) to draw circle. // We preserve backward-compatibility. if (!aclockwise && startAngle > endAngle) { newEndAngle = startAngle + (TWO_PI - ((startAngle - endAngle) % TWO_PI)); } else if (aclockwise && startAngle < endAngle) { newEndAngle = startAngle - (TWO_PI - ((endAngle - startAngle) % TWO_PI)); } path.append(new Arc2D((float) (x - r), (float) (y - r), (float) (2 * r), (float) (2 * r), (float) Math.toDegrees(-startAngle), (float) Math.toDegrees(startAngle - newEndAngle), Arc2D.OPEN), true); } public boolean contains(int rule, double x, double y) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).contains({1},{2},{3})", new Object[] {getID(), rule, x, y}); } final int savedRule = path.getWindingRule(); path.setWindingRule(rule); final boolean res = path.contains((float)x, (float)y); path.setWindingRule(savedRule); return res; } @Override public WCRectangle getBounds() { RectBounds b = path.getBounds(); return new WCRectangle(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight()); } public void clear() { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).clear()", getID()); } hasCP = false; path.reset(); } public void moveTo(double x, double y) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).moveTo({1},{2})", new Object[] {getID(), x, y}); } hasCP = true; path.moveTo((float)x, (float)y); } public void addLineTo(double x, double y) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).addLineTo({1},{2})", new Object[] {getID(), x, y}); } hasCP = true; path.lineTo((float)x, (float)y); } public void addQuadCurveTo(double x0, double y0, double x1, double y1) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).addQuadCurveTo({1},{2},{3},{4})", new Object[] {getID(), x0, y0, x1, y1}); } hasCP = true; path.quadTo((float)x0, (float)y0, (float)x1, (float)y1); } public void addBezierCurveTo(double x0, double y0, double x1, double y1, double x2, double y2) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).addBezierCurveTo({1},{2},{3},{4},{5},{6})", new Object[] {getID(), x0, y0, x1, y1, x2, y2}); } hasCP = true; path.curveTo((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); } public void addPath(WCPath p) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).addPath({1})", new Object[] {getID(), p.getID()}); } hasCP = hasCP || ((WCPathImpl)p).hasCP; path.append(((WCPathImpl)p).path, false); } public void closeSubpath() { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).closeSubpath()", getID()); } path.closePath(); } public boolean hasCurrentPoint() { return hasCP; } public boolean isEmpty() { PathIterator pi = path.getPathIterator(null); float [] coords = new float[6]; while(!pi.isDone()) { switch(pi.currentSegment(coords)) { case PathIterator.SEG_LINETO: case PathIterator.SEG_QUADTO: case PathIterator.SEG_CUBICTO: return false; } pi.next(); } return true; } public int getWindingRule() { return 1 - this.path.getWindingRule(); // convert prism to webkit } public void setWindingRule(int rule) { this.path.setWindingRule(1 - rule); // convert webkit to prism } public Path2D getPlatformPath() { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).getPath() BEGIN=====", getID()); PathIterator pi = path.getPathIterator(null); float [] coords = new float[6]; while(!pi.isDone()) { switch(pi.currentSegment(coords)) { case PathIterator.SEG_MOVETO: log.fine("SEG_MOVETO ({0},{1})", new Object[] {coords[0], coords[1]}); break; case PathIterator.SEG_LINETO: log.fine("SEG_LINETO ({0},{1})", new Object[] {coords[0], coords[1]}); break; case PathIterator.SEG_QUADTO: log.fine("SEG_QUADTO ({0},{1},{2},{3})", new Object[] {coords[0], coords[1], coords[2], coords[3]}); break; case PathIterator.SEG_CUBICTO: log.fine("SEG_CUBICTO ({0},{1},{2},{3},{4},{5})", new Object[] {coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]}); break; case PathIterator.SEG_CLOSE: log.fine("SEG_CLOSE"); break; } pi.next(); } log.fine("========getPath() END====="); } return path; } public WCPathIterator getPathIterator() { final PathIterator pi = path.getPathIterator(null); return new WCPathIterator() { @Override public int getWindingRule() { return pi.getWindingRule(); } @Override public boolean isDone() { return pi.isDone(); } @Override public void next() { pi.next(); } @Override public int currentSegment(double[] coords) { float [] _coords = new float[6]; int segmentType = pi.currentSegment(_coords); for (int i = 0; i < coords.length; i++) { coords[i] = _coords[i]; } return segmentType; } }; } public void translate(double x, double y) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).translate({1}, {2})", new Object[] {getID(), x, y}); } path.transform(BaseTransform.getTranslateInstance(x, y)); } public void transform(double mxx, double myx, double mxy, double myy, double mxt, double myt) { if (log.isLoggable(Level.FINE)) { log.fine("WCPathImpl({0}).transform({1},{2},{3},{4},{5},{6})", new Object[] {getID(), mxx, myx, mxy, myy, mxt, myt}); } path.transform(BaseTransform.getInstance(mxx, myx, mxy, myy, mxt, myt)); } }