4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/Apackage sun.java2d;
4632N/A
4632N/Aimport java.awt.*;
4632N/Aimport java.awt.font.*;
4632N/Aimport java.awt.geom.*;
4632N/Aimport java.awt.image.*;
4632N/A
4632N/Aimport sun.awt.image.*;
4632N/Aimport sun.java2d.loops.*;
4632N/Aimport sun.java2d.pipe.*;
4632N/A
4632N/Apublic class CompositeCRenderer extends CRenderer implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe, DrawImagePipe, TextPipe {
4632N/A final static int fPadding = 4;
4632N/A final static int fPaddingHalf = fPadding / 2;
4632N/A
4632N/A private static AffineTransform sIdentityMatrix = new AffineTransform();
4632N/A
4632N/A AffineTransform ShapeTM = new AffineTransform();
4632N/A Rectangle2D ShapeBounds = new Rectangle2D.Float();
4632N/A
4632N/A Line2D line = new Line2D.Float();
4632N/A Rectangle2D rectangle = new Rectangle2D.Float();
4632N/A RoundRectangle2D roundrectangle = new RoundRectangle2D.Float();
4632N/A Ellipse2D ellipse = new Ellipse2D.Float();
4632N/A Arc2D arc = new Arc2D.Float();
4632N/A
4632N/A public synchronized void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) {
4632N/A // create shape corresponding to this primitive
4632N/A line.setLine(x1, y1, x2, y2);
4632N/A
4632N/A draw(sg2d, line);
4632N/A }
4632N/A
4632N/A public synchronized void drawRect(SunGraphics2D sg2d, int x, int y, int width, int height) {
4632N/A // create shape corresponding to this primitive
4632N/A rectangle.setRect(x, y, width, height);
4632N/A
4632N/A draw(sg2d, rectangle);
4632N/A }
4632N/A
4632N/A public synchronized void drawRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) {
4632N/A // create shape corresponding to this primitive
4632N/A roundrectangle.setRoundRect(x, y, width, height, arcWidth, arcHeight);
4632N/A
4632N/A draw(sg2d, roundrectangle);
4632N/A }
4632N/A
4632N/A public synchronized void drawOval(SunGraphics2D sg2d, int x, int y, int width, int height) {
4632N/A // create shape corresponding to this primitive
4632N/A ellipse.setFrame(x, y, width, height);
4632N/A
4632N/A draw(sg2d, ellipse);
4632N/A }
4632N/A
4632N/A public synchronized void drawArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) {
4632N/A // create shape corresponding to this primitive
4632N/A arc.setArc(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN);
4632N/A
4632N/A draw(sg2d, arc);
4632N/A }
4632N/A
4632N/A public synchronized void drawPolyline(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints) {
4632N/A doPolygon(sg2d, xpoints, ypoints, npoints, false, false);
4632N/A }
4632N/A
4632N/A public synchronized void drawPolygon(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints) {
4632N/A doPolygon(sg2d, xpoints, ypoints, npoints, true, false);
4632N/A }
4632N/A
4632N/A public synchronized void fillRect(SunGraphics2D sg2d, int x, int y, int width, int height) {
4632N/A // create shape corresponding to this primitive
4632N/A rectangle.setRect(x, y, width, height);
4632N/A
4632N/A fill(sg2d, rectangle);
4632N/A }
4632N/A
4632N/A public synchronized void fillRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) {
4632N/A // create shape corresponding to this primitive
4632N/A roundrectangle.setRoundRect(x, y, width, height, arcWidth, arcHeight);
4632N/A
4632N/A fill(sg2d, roundrectangle);
4632N/A }
4632N/A
4632N/A public synchronized void fillOval(SunGraphics2D sg2d, int x, int y, int width, int height) {
4632N/A // create shape corresponding to this primitive
4632N/A ellipse.setFrame(x, y, width, height);
4632N/A
4632N/A fill(sg2d, ellipse);
4632N/A }
4632N/A
4632N/A public synchronized void fillArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) {
4632N/A // create shape corresponding to this primitive
4632N/A arc.setArc(x, y, width, height, startAngle, arcAngle, Arc2D.PIE);
4632N/A
4632N/A fill(sg2d, arc);
4632N/A }
4632N/A
4632N/A public synchronized void fillPolygon(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints) {
4632N/A doPolygon(sg2d, xpoints, ypoints, npoints, true, true);
4632N/A }
4632N/A
4632N/A public synchronized void doPolygon(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints, boolean ispolygon, boolean isfill) {
4632N/A GeneralPath gp = new GeneralPath(Path2D.WIND_NON_ZERO, npoints);
4632N/A gp.moveTo(xpoints[0], ypoints[0]);
4632N/A for (int i = 1; i < npoints; i++) {
4632N/A gp.lineTo(xpoints[i], ypoints[i]);
4632N/A }
4632N/A if (ispolygon) {
4632N/A // according to the specs (only applies to polygons, not polylines)
4632N/A if ((xpoints[0] != xpoints[npoints - 1]) || (ypoints[0] != ypoints[npoints - 1])) {
4632N/A gp.lineTo(xpoints[0], ypoints[0]);
4632N/A }
4632N/A }
4632N/A
4632N/A doShape(sg2d, (OSXSurfaceData) sg2d.getSurfaceData(), (Shape) gp, isfill);
4632N/A }
4632N/A
4632N/A public synchronized void draw(SunGraphics2D sg2d, Shape shape) {
4632N/A doShape(sg2d, (OSXSurfaceData) sg2d.getSurfaceData(), shape, false);
4632N/A }
4632N/A
4632N/A public synchronized void fill(SunGraphics2D sg2d, Shape shape) {
4632N/A doShape(sg2d, (OSXSurfaceData) sg2d.getSurfaceData(), shape, true);
4632N/A }
4632N/A
4632N/A void doShape(SunGraphics2D sg2d, OSXSurfaceData surfaceData, Shape shape, boolean isfill) {
4632N/A Rectangle2D shapeBounds = shape.getBounds2D();
4632N/A
4632N/A // We don't want to draw with negative width and height (CRender doesn't do it and Windows doesn't do it either)
4632N/A // Drawing with negative w and h, can cause CG problems down the line <rdar://3960579> (vm)
4632N/A if ((shapeBounds.getWidth() < 0) || (shapeBounds.getHeight() < 0)) { return; }
4632N/A
4632N/A // get final destination compositing bounds (after all transformations if needed)
4632N/A Rectangle2D compositingBounds = padBounds(sg2d, shape);
4632N/A
4632N/A // constrain the bounds to be within surface bounds
4632N/A clipBounds(sg2d, compositingBounds);
4632N/A
4632N/A // if the compositing region is empty we skip all remaining compositing work:
4632N/A if (compositingBounds.isEmpty() == false) {
4632N/A BufferedImage srcPixels;
4632N/A // create a matching surface into which we'll render the primitive to be composited
4632N/A // with the desired dimension
4632N/A srcPixels = surfaceData.getCompositingSrcImage((int) (compositingBounds.getWidth()),
4632N/A (int) (compositingBounds.getHeight()));
4632N/A
4632N/A Graphics2D g = srcPixels.createGraphics();
4632N/A
4632N/A // sync up graphics state
4632N/A ShapeTM.setToTranslation(-compositingBounds.getX(), -compositingBounds.getY());
4632N/A ShapeTM.concatenate(sg2d.transform);
4632N/A g.setTransform(ShapeTM);
4632N/A g.setRenderingHints(sg2d.getRenderingHints());
4632N/A g.setPaint(sg2d.getPaint());
4632N/A g.setStroke(sg2d.getStroke());
4632N/A
4632N/A // render the primitive to be composited
4632N/A if (isfill) {
4632N/A g.fill(shape);
4632N/A } else {
4632N/A g.draw(shape);
4632N/A }
4632N/A
4632N/A g.dispose();
4632N/A
4632N/A composite(sg2d, surfaceData, srcPixels, compositingBounds);
4632N/A }
4632N/A }
4632N/A
4632N/A public synchronized void drawString(SunGraphics2D sg2d, String str, double x, double y) {
4632N/A drawGlyphVector(sg2d, sg2d.getFont().createGlyphVector(sg2d.getFontRenderContext(), str), x, y);
4632N/A }
4632N/A
4632N/A public synchronized void drawChars(SunGraphics2D sg2d, char data[], int offset, int length, int x, int y) {
4632N/A drawString(sg2d, new String(data, offset, length), x, y);
4632N/A }
4632N/A
4632N/A public synchronized void drawGlyphVector(SunGraphics2D sg2d, GlyphVector glyphVector, double x, double y) {
4632N/A drawGlyphVector(sg2d, glyphVector, (float) x, (float) y);
4632N/A }
4632N/A
4632N/A public synchronized void drawGlyphVector(SunGraphics2D sg2d, GlyphVector glyphVector, float x, float y) {
4632N/A OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData();
4632N/A
4632N/A Shape shape = glyphVector.getOutline(x, y);
4632N/A
4632N/A // get final destination compositing bounds (after all transformations if needed)
4632N/A Rectangle2D compositingBounds = padBounds(sg2d, shape);
4632N/A
4632N/A // constrain the bounds to be within surface bounds
4632N/A clipBounds(sg2d, compositingBounds);
4632N/A
4632N/A // if the compositing region is empty we skip all remaining compositing work:
4632N/A if (compositingBounds.isEmpty() == false) {
4632N/A BufferedImage srcPixels;
4632N/A {
4632N/A // create matching image into which we'll render the primitive to be composited
4632N/A srcPixels = surfaceData.getCompositingSrcImage((int) compositingBounds.getWidth(), (int) compositingBounds.getHeight());
4632N/A
4632N/A Graphics2D g = srcPixels.createGraphics();
4632N/A
4632N/A // sync up graphics state
4632N/A ShapeTM.setToTranslation(-compositingBounds.getX(), -compositingBounds.getY());
4632N/A ShapeTM.concatenate(sg2d.transform);
4632N/A g.setTransform(ShapeTM);
4632N/A g.setPaint(sg2d.getPaint());
4632N/A g.setStroke(sg2d.getStroke());
4632N/A g.setFont(sg2d.getFont());
4632N/A g.setRenderingHints(sg2d.getRenderingHints());
4632N/A
4632N/A // render the primitive to be composited
4632N/A g.drawGlyphVector(glyphVector, x, y);
4632N/A g.dispose();
4632N/A }
4632N/A
4632N/A composite(sg2d, surfaceData, srcPixels, compositingBounds);
4632N/A }
4632N/A }
4632N/A
4632N/A protected boolean blitImage(SunGraphics2D sg2d, Image img, boolean fliph, boolean flipv, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, Color bgColor) {
4632N/A OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData();
4632N/A
4632N/A // get final destination compositing bounds (after all transformations if needed)
4632N/A dx = (flipv == false) ? dx : dx - dw;
4632N/A dy = (fliph == false) ? dy : dy - dh;
4632N/A ShapeBounds.setFrame(dx, dy, dw, dh);
4632N/A Rectangle2D compositingBounds = ShapeBounds;
4632N/A boolean complexTransform = (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE);
4632N/A if (complexTransform == false) {
4632N/A double newX = Math.floor(compositingBounds.getX() + sg2d.transX);
4632N/A double newY = Math.floor(compositingBounds.getY() + sg2d.transY);
4632N/A double newW = Math.ceil(compositingBounds.getWidth()) + (newX < compositingBounds.getX() ? 1 : 0);
4632N/A double newH = Math.ceil(compositingBounds.getHeight()) + (newY < compositingBounds.getY() ? 1 : 0);
4632N/A compositingBounds.setRect(newX, newY, newW, newH);
4632N/A } else {
4632N/A Shape transformedShape = sg2d.transform.createTransformedShape(compositingBounds);
4632N/A compositingBounds = transformedShape.getBounds2D();
4632N/A double newX = Math.floor(compositingBounds.getX());
4632N/A double newY = Math.floor(compositingBounds.getY());
4632N/A double newW = Math.ceil(compositingBounds.getWidth()) + (newX < compositingBounds.getX() ? 1 : 0);
4632N/A double newH = Math.ceil(compositingBounds.getHeight()) + (newY < compositingBounds.getY() ? 1 : 0);
4632N/A compositingBounds.setRect(newX, newY, newW, newH);
4632N/A }
4632N/A
4632N/A // constrain the bounds to be within surface bounds
4632N/A clipBounds(sg2d, compositingBounds);
4632N/A
4632N/A // if the compositing region is empty we skip all remaining compositing work:
4632N/A if (compositingBounds.isEmpty() == false) {
4632N/A BufferedImage srcPixels;
4632N/A {
4632N/A // create matching image into which we'll render the primitive to be composited
4632N/A srcPixels = surfaceData.getCompositingSrcImage((int) compositingBounds.getWidth(), (int) compositingBounds.getHeight());
4632N/A
4632N/A Graphics2D g = srcPixels.createGraphics();
4632N/A
4632N/A // sync up graphics state
4632N/A ShapeTM.setToTranslation(-compositingBounds.getX(), -compositingBounds.getY());
4632N/A ShapeTM.concatenate(sg2d.transform);
4632N/A g.setTransform(ShapeTM);
4632N/A g.setRenderingHints(sg2d.getRenderingHints());
4632N/A g.setComposite(AlphaComposite.Src);
4632N/A
4632N/A int sx2 = (flipv == false) ? sx + sw : sx - sw;
4632N/A int sy2 = (fliph == false) ? sy + sh : sy - sh;
4632N/A g.drawImage(img, dx, dy, dx + dw, dy + dh, sx, sy, sx2, sy2, null);
4632N/A
4632N/A g.dispose();
4632N/A }
4632N/A
4632N/A composite(sg2d, surfaceData, srcPixels, compositingBounds);
4632N/A }
4632N/A
4632N/A return true;
4632N/A }
4632N/A
4632N/A Rectangle2D padBounds(SunGraphics2D sg2d, Shape shape) {
4632N/A shape = sg2d.transformShape(shape);
4632N/A
4632N/A int paddingHalf = fPaddingHalf;
4632N/A int padding = fPadding;
4632N/A if (sg2d.stroke != null) {
4632N/A if (sg2d.stroke instanceof BasicStroke) {
4632N/A int width = (int) (((BasicStroke) sg2d.stroke).getLineWidth() + 0.5f);
4632N/A int widthHalf = width / 2 + 1;
4632N/A paddingHalf += widthHalf;
4632N/A padding += 2 * widthHalf;
4632N/A } else {
4632N/A shape = sg2d.stroke.createStrokedShape(shape);
4632N/A }
4632N/A }
4632N/A Rectangle2D bounds = shape.getBounds2D();
4632N/A bounds.setRect(bounds.getX() - paddingHalf, bounds.getY() - paddingHalf, bounds.getWidth() + padding, bounds.getHeight() + padding);
4632N/A
4632N/A double newX = Math.floor(bounds.getX());
4632N/A double newY = Math.floor(bounds.getY());
4632N/A double newW = Math.ceil(bounds.getWidth()) + (newX < bounds.getX() ? 1 : 0);
4632N/A double newH = Math.ceil(bounds.getHeight()) + (newY < bounds.getY() ? 1 : 0);
4632N/A bounds.setRect(newX, newY, newW, newH);
4632N/A
4632N/A return bounds;
4632N/A }
4632N/A
4632N/A void clipBounds(SunGraphics2D sg2d, Rectangle2D bounds) {
4632N/A /*
4632N/A * System.err.println("clipBounds"); System.err.println(" transform="+sg2d.transform);
4632N/A * System.err.println(" getTransform()="+sg2d.getTransform());
4632N/A * System.err.println(" complexTransform="+(sg2d.transformState > SunGraphics2D.TRANSFORM_TRANSLATESCALE));
4632N/A * System.err.println(" transX="+sg2d.transX+" transY="+sg2d.transX);
4632N/A * System.err.println(" sg2d.constrainClip="+sg2d.constrainClip); if (sg2d.constrainClip != null) {
4632N/A * System.err
4632N/A * .println(" constrainClip: x="+sg2d.constrainClip.getLoX()+" y="+sg2d.constrainClip.getLoY()+" w="
4632N/A * +sg2d.constrainClip.getWidth()+" h="+sg2d.constrainClip.getHeight());}
4632N/A * System.err.println(" constrainX="+sg2d.constrainX+" constrainY="+sg2d.constrainY);
4632N/A * System.err.println(" usrClip="+sg2d.usrClip);
4632N/A * System.err.println(" devClip: x="+sg2d.devClip.getLoX()+" y="
4632N/A * +sg2d.devClip.getLoY()+" w="+sg2d.devClip.getWidth()+" h="+sg2d.devClip.getHeight());
4632N/A */
4632N/A Region intersection = sg2d.clipRegion.getIntersectionXYWH((int) bounds.getX(), (int) bounds.getY(), (int) bounds.getWidth(), (int) bounds.getHeight());
4632N/A bounds.setRect(intersection.getLoX(), intersection.getLoY(), intersection.getWidth(), intersection.getHeight());
4632N/A }
4632N/A
4632N/A BufferedImage getSurfacePixels(SunGraphics2D sg2d, OSXSurfaceData surfaceData, int x, int y, int w, int h) {
4632N/A // create an image to copy the surface pixels into
4632N/A BufferedImage dstInPixels = surfaceData.getCompositingDstInImage(w, h);
4632N/A
4632N/A // get the pixels from the dst surface
4632N/A return surfaceData.copyArea(sg2d, x, y, w, h, dstInPixels);
4632N/A }
4632N/A
4632N/A void composite(SunGraphics2D sg2d, OSXSurfaceData surfaceData, BufferedImage srcPixels, Rectangle2D compositingBounds) {
4632N/A // Thread.dumpStack();
4632N/A // System.err.println("composite");
4632N/A // System.err.println(" compositingBounds="+compositingBounds);
4632N/A int x = (int) compositingBounds.getX();
4632N/A int y = (int) compositingBounds.getY();
4632N/A int w = (int) compositingBounds.getWidth();
4632N/A int h = (int) compositingBounds.getHeight();
4632N/A
4632N/A boolean succeded = false;
4632N/A
4632N/A Composite composite = sg2d.getComposite();
4632N/A if (composite instanceof XORComposite) {
4632N/A // 1st native XOR try
4632N/A // we try to perform XOR using surface pixels directly
4632N/A try {
4632N/A succeded = surfaceData.xorSurfacePixels(sg2d, srcPixels, x, y, w, h, ((XORComposite) composite).getXorColor().getRGB());
4632N/A } catch (Exception e) {
4632N/A succeded = false;
4632N/A }
4632N/A }
4632N/A
4632N/A if (succeded == false) {
4632N/A // create image with the original pixels of surface
4632N/A BufferedImage dstInPixels = getSurfacePixels(sg2d, surfaceData, x, y, w, h);
4632N/A BufferedImage dstOutPixels = null;
4632N/A
4632N/A if (composite instanceof XORComposite) {
4632N/A // 2nd native XOR try
4632N/A // we try to perform XOR on image's pixels (which were copied from surface first)
4632N/A try {
4632N/A OSXSurfaceData osxsd = (OSXSurfaceData) (BufImgSurfaceData.createData(dstInPixels));
4632N/A succeded = osxsd.xorSurfacePixels(sg2d, srcPixels, 0, 0, w, h, ((XORComposite) composite).getXorColor().getRGB());
4632N/A dstOutPixels = dstInPixels;
4632N/A } catch (Exception e) {
4632N/A succeded = false;
4632N/A }
4632N/A }
4632N/A
4632N/A // either 2nd native XOR failed OR we have a case of custom compositing
4632N/A if (succeded == false) {
4632N/A // create an image into which we'll composite result: we MUST use a different destination (compositing
4632N/A // is NOT "in place" operation)
4632N/A dstOutPixels = surfaceData.getCompositingDstOutImage(w, h);
4632N/A
4632N/A // prepare rasters for compositing
4632N/A WritableRaster srcRaster = srcPixels.getRaster();
4632N/A WritableRaster dstInRaster = dstInPixels.getRaster();
4632N/A WritableRaster dstOutRaster = dstOutPixels.getRaster();
4632N/A
4632N/A CompositeContext compositeContext = composite.createContext(srcPixels.getColorModel(), dstOutPixels.getColorModel(), sg2d.getRenderingHints());
4632N/A compositeContext.compose(srcRaster, dstInRaster, dstOutRaster);
4632N/A compositeContext.dispose();
4632N/A
4632N/A // gznote: radar bug number
4632N/A // "cut out" the shape we're interested in
4632N/A // applyMask(BufImgSurfaceData.createData(dstOutPixels), BufImgSurfaceData.createData(srcPixels), w, h);
4632N/A }
4632N/A
4632N/A // blit the results back to the dst surface
4632N/A Composite savedComposite = sg2d.getComposite();
4632N/A AffineTransform savedTM = sg2d.getTransform();
4632N/A int savedCX = sg2d.constrainX;
4632N/A int savedCY = sg2d.constrainY;
4632N/A {
4632N/A sg2d.setComposite(AlphaComposite.SrcOver);
4632N/A // all the compositing is done in the coordinate space of the component. the x and the y are the
4632N/A // position of that component in the surface
4632N/A // so we need to set the sg2d.transform to identity and we must set the contrainX/Y to 0 for the
4632N/A // setTransform() to not be constrained
4632N/A sg2d.constrainX = 0;
4632N/A sg2d.constrainY = 0;
4632N/A sg2d.setTransform(sIdentityMatrix);
4632N/A sg2d.drawImage(dstOutPixels, x, y, x + w, y + h, 0, 0, w, h, null);
4632N/A }
4632N/A sg2d.constrainX = savedCX;
4632N/A sg2d.constrainY = savedCY;
4632N/A sg2d.setTransform(savedTM);
4632N/A sg2d.setComposite(savedComposite);
4632N/A }
4632N/A }
4632N/A}