Path: blob/master/src/java.desktop/macosx/classes/sun/java2d/CompositeCRenderer.java
41152 views
/*1* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d;2627import java.awt.*;28import java.awt.font.*;29import java.awt.geom.*;30import java.awt.image.*;3132import sun.awt.image.*;33import sun.java2d.loops.*;34import sun.java2d.pipe.*;3536public class CompositeCRenderer extends CRenderer implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe, DrawImagePipe, TextPipe {37static final int fPadding = 4;38static final int fPaddingHalf = fPadding / 2;3940private static AffineTransform sIdentityMatrix = new AffineTransform();4142AffineTransform ShapeTM = new AffineTransform();43Rectangle2D ShapeBounds = new Rectangle2D.Float();4445Line2D line = new Line2D.Float();46Rectangle2D rectangle = new Rectangle2D.Float();47RoundRectangle2D roundrectangle = new RoundRectangle2D.Float();48Ellipse2D ellipse = new Ellipse2D.Float();49Arc2D arc = new Arc2D.Float();5051public synchronized void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) {52// create shape corresponding to this primitive53line.setLine(x1, y1, x2, y2);5455draw(sg2d, line);56}5758public synchronized void drawRect(SunGraphics2D sg2d, int x, int y, int width, int height) {59// create shape corresponding to this primitive60rectangle.setRect(x, y, width, height);6162draw(sg2d, rectangle);63}6465public synchronized void drawRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) {66// create shape corresponding to this primitive67roundrectangle.setRoundRect(x, y, width, height, arcWidth, arcHeight);6869draw(sg2d, roundrectangle);70}7172public synchronized void drawOval(SunGraphics2D sg2d, int x, int y, int width, int height) {73// create shape corresponding to this primitive74ellipse.setFrame(x, y, width, height);7576draw(sg2d, ellipse);77}7879public synchronized void drawArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) {80// create shape corresponding to this primitive81arc.setArc(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN);8283draw(sg2d, arc);84}8586public synchronized void drawPolyline(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints) {87doPolygon(sg2d, xpoints, ypoints, npoints, false, false);88}8990public synchronized void drawPolygon(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints) {91doPolygon(sg2d, xpoints, ypoints, npoints, true, false);92}9394public synchronized void fillRect(SunGraphics2D sg2d, int x, int y, int width, int height) {95// create shape corresponding to this primitive96rectangle.setRect(x, y, width, height);9798fill(sg2d, rectangle);99}100101public synchronized void fillRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) {102// create shape corresponding to this primitive103roundrectangle.setRoundRect(x, y, width, height, arcWidth, arcHeight);104105fill(sg2d, roundrectangle);106}107108public synchronized void fillOval(SunGraphics2D sg2d, int x, int y, int width, int height) {109// create shape corresponding to this primitive110ellipse.setFrame(x, y, width, height);111112fill(sg2d, ellipse);113}114115public synchronized void fillArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) {116// create shape corresponding to this primitive117arc.setArc(x, y, width, height, startAngle, arcAngle, Arc2D.PIE);118119fill(sg2d, arc);120}121122public synchronized void fillPolygon(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints) {123doPolygon(sg2d, xpoints, ypoints, npoints, true, true);124}125126public synchronized void doPolygon(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints, boolean ispolygon, boolean isfill) {127GeneralPath gp = new GeneralPath(Path2D.WIND_NON_ZERO, npoints);128gp.moveTo(xpoints[0], ypoints[0]);129for (int i = 1; i < npoints; i++) {130gp.lineTo(xpoints[i], ypoints[i]);131}132if (ispolygon) {133// according to the specs (only applies to polygons, not polylines)134if ((xpoints[0] != xpoints[npoints - 1]) || (ypoints[0] != ypoints[npoints - 1])) {135gp.lineTo(xpoints[0], ypoints[0]);136}137}138139doShape(sg2d, (OSXSurfaceData) sg2d.getSurfaceData(), (Shape) gp, isfill);140}141142public synchronized void draw(SunGraphics2D sg2d, Shape shape) {143doShape(sg2d, (OSXSurfaceData) sg2d.getSurfaceData(), shape, false);144}145146public synchronized void fill(SunGraphics2D sg2d, Shape shape) {147doShape(sg2d, (OSXSurfaceData) sg2d.getSurfaceData(), shape, true);148}149150void doShape(SunGraphics2D sg2d, OSXSurfaceData surfaceData, Shape shape, boolean isfill) {151Rectangle2D shapeBounds = shape.getBounds2D();152153// We don't want to draw with negative width and height (CRender doesn't do it and Windows doesn't do it either)154// Drawing with negative w and h, can cause CG problems down the line <rdar://3960579> (vm)155if ((shapeBounds.getWidth() < 0) || (shapeBounds.getHeight() < 0)) { return; }156157// get final destination compositing bounds (after all transformations if needed)158Rectangle2D compositingBounds = padBounds(sg2d, shape);159160// constrain the bounds to be within surface bounds161clipBounds(sg2d, compositingBounds);162163// if the compositing region is empty we skip all remaining compositing work:164if (compositingBounds.isEmpty() == false) {165BufferedImage srcPixels;166// create a matching surface into which we'll render the primitive to be composited167// with the desired dimension168srcPixels = surfaceData.getCompositingSrcImage((int) (compositingBounds.getWidth()),169(int) (compositingBounds.getHeight()));170171Graphics2D g = srcPixels.createGraphics();172173// sync up graphics state174ShapeTM.setToTranslation(-compositingBounds.getX(), -compositingBounds.getY());175ShapeTM.concatenate(sg2d.transform);176g.setTransform(ShapeTM);177g.setRenderingHints(sg2d.getRenderingHints());178g.setPaint(sg2d.getPaint());179g.setStroke(sg2d.getStroke());180181// render the primitive to be composited182if (isfill) {183g.fill(shape);184} else {185g.draw(shape);186}187188g.dispose();189190composite(sg2d, surfaceData, srcPixels, compositingBounds);191}192}193194public synchronized void drawString(SunGraphics2D sg2d, String str, double x, double y) {195drawGlyphVector(sg2d, sg2d.getFont().createGlyphVector(sg2d.getFontRenderContext(), str), x, y);196}197198public synchronized void drawChars(SunGraphics2D sg2d, char[] data, int offset, int length, int x, int y) {199drawString(sg2d, new String(data, offset, length), x, y);200}201202public synchronized void drawGlyphVector(SunGraphics2D sg2d, GlyphVector glyphVector, double x, double y) {203drawGlyphVector(sg2d, glyphVector, (float) x, (float) y);204}205206public synchronized void drawGlyphVector(SunGraphics2D sg2d, GlyphVector glyphVector, float x, float y) {207OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData();208209Shape shape = glyphVector.getOutline(x, y);210211// get final destination compositing bounds (after all transformations if needed)212Rectangle2D compositingBounds = padBounds(sg2d, shape);213214// constrain the bounds to be within surface bounds215clipBounds(sg2d, compositingBounds);216217// if the compositing region is empty we skip all remaining compositing work:218if (compositingBounds.isEmpty() == false) {219BufferedImage srcPixels;220{221// create matching image into which we'll render the primitive to be composited222srcPixels = surfaceData.getCompositingSrcImage((int) compositingBounds.getWidth(), (int) compositingBounds.getHeight());223224Graphics2D g = srcPixels.createGraphics();225226// sync up graphics state227ShapeTM.setToTranslation(-compositingBounds.getX(), -compositingBounds.getY());228ShapeTM.concatenate(sg2d.transform);229g.setTransform(ShapeTM);230g.setPaint(sg2d.getPaint());231g.setStroke(sg2d.getStroke());232g.setFont(sg2d.getFont());233g.setRenderingHints(sg2d.getRenderingHints());234235// render the primitive to be composited236g.drawGlyphVector(glyphVector, x, y);237g.dispose();238}239240composite(sg2d, surfaceData, srcPixels, compositingBounds);241}242}243244protected 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) {245OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData();246247// get final destination compositing bounds (after all transformations if needed)248dx = (flipv == false) ? dx : dx - dw;249dy = (fliph == false) ? dy : dy - dh;250ShapeBounds.setFrame(dx, dy, dw, dh);251Rectangle2D compositingBounds = ShapeBounds;252boolean complexTransform = (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE);253if (complexTransform == false) {254double newX = Math.floor(compositingBounds.getX() + sg2d.transX);255double newY = Math.floor(compositingBounds.getY() + sg2d.transY);256double newW = Math.ceil(compositingBounds.getWidth()) + (newX < compositingBounds.getX() ? 1 : 0);257double newH = Math.ceil(compositingBounds.getHeight()) + (newY < compositingBounds.getY() ? 1 : 0);258compositingBounds.setRect(newX, newY, newW, newH);259} else {260Shape transformedShape = sg2d.transform.createTransformedShape(compositingBounds);261compositingBounds = transformedShape.getBounds2D();262double newX = Math.floor(compositingBounds.getX());263double newY = Math.floor(compositingBounds.getY());264double newW = Math.ceil(compositingBounds.getWidth()) + (newX < compositingBounds.getX() ? 1 : 0);265double newH = Math.ceil(compositingBounds.getHeight()) + (newY < compositingBounds.getY() ? 1 : 0);266compositingBounds.setRect(newX, newY, newW, newH);267}268269// constrain the bounds to be within surface bounds270clipBounds(sg2d, compositingBounds);271272// if the compositing region is empty we skip all remaining compositing work:273if (compositingBounds.isEmpty() == false) {274BufferedImage srcPixels;275{276// create matching image into which we'll render the primitive to be composited277srcPixels = surfaceData.getCompositingSrcImage((int) compositingBounds.getWidth(), (int) compositingBounds.getHeight());278279Graphics2D g = srcPixels.createGraphics();280281// sync up graphics state282ShapeTM.setToTranslation(-compositingBounds.getX(), -compositingBounds.getY());283ShapeTM.concatenate(sg2d.transform);284g.setTransform(ShapeTM);285g.setRenderingHints(sg2d.getRenderingHints());286g.setComposite(AlphaComposite.Src);287288int sx2 = (flipv == false) ? sx + sw : sx - sw;289int sy2 = (fliph == false) ? sy + sh : sy - sh;290g.drawImage(img, dx, dy, dx + dw, dy + dh, sx, sy, sx2, sy2, null);291292g.dispose();293}294295composite(sg2d, surfaceData, srcPixels, compositingBounds);296}297298return true;299}300301Rectangle2D padBounds(SunGraphics2D sg2d, Shape shape) {302shape = sg2d.transformShape(shape);303304int paddingHalf = fPaddingHalf;305int padding = fPadding;306if (sg2d.stroke != null) {307if (sg2d.stroke instanceof BasicStroke) {308int width = (int) (((BasicStroke) sg2d.stroke).getLineWidth() + 0.5f);309int widthHalf = width / 2 + 1;310paddingHalf += widthHalf;311padding += 2 * widthHalf;312} else {313shape = sg2d.stroke.createStrokedShape(shape);314}315}316Rectangle2D bounds = shape.getBounds2D();317bounds.setRect(bounds.getX() - paddingHalf, bounds.getY() - paddingHalf, bounds.getWidth() + padding, bounds.getHeight() + padding);318319double newX = Math.floor(bounds.getX());320double newY = Math.floor(bounds.getY());321double newW = Math.ceil(bounds.getWidth()) + (newX < bounds.getX() ? 1 : 0);322double newH = Math.ceil(bounds.getHeight()) + (newY < bounds.getY() ? 1 : 0);323bounds.setRect(newX, newY, newW, newH);324325return bounds;326}327328void clipBounds(SunGraphics2D sg2d, Rectangle2D bounds) {329/*330* System.err.println("clipBounds"); System.err.println(" transform="+sg2d.transform);331* System.err.println(" getTransform()="+sg2d.getTransform());332* System.err.println(" complexTransform="+(sg2d.transformState > SunGraphics2D.TRANSFORM_TRANSLATESCALE));333* System.err.println(" transX="+sg2d.transX+" transY="+sg2d.transX);334* System.err.println(" sg2d.constrainClip="+sg2d.constrainClip); if (sg2d.constrainClip != null) {335* System.err336* .println(" constrainClip: x="+sg2d.constrainClip.getLoX()+" y="+sg2d.constrainClip.getLoY()+" w="337* +sg2d.constrainClip.getWidth()+" h="+sg2d.constrainClip.getHeight());}338* System.err.println(" constrainX="+sg2d.constrainX+" constrainY="+sg2d.constrainY);339* System.err.println(" usrClip="+sg2d.usrClip);340* System.err.println(" devClip: x="+sg2d.devClip.getLoX()+" y="341* +sg2d.devClip.getLoY()+" w="+sg2d.devClip.getWidth()+" h="+sg2d.devClip.getHeight());342*/343Region intersection = sg2d.clipRegion.getIntersectionXYWH((int) bounds.getX(), (int) bounds.getY(), (int) bounds.getWidth(), (int) bounds.getHeight());344bounds.setRect(intersection.getLoX(), intersection.getLoY(), intersection.getWidth(), intersection.getHeight());345}346347BufferedImage getSurfacePixels(SunGraphics2D sg2d, OSXSurfaceData surfaceData, int x, int y, int w, int h) {348// create an image to copy the surface pixels into349BufferedImage dstInPixels = surfaceData.getCompositingDstInImage(w, h);350351// get the pixels from the dst surface352return surfaceData.copyArea(sg2d, x, y, w, h, dstInPixels);353}354355void composite(SunGraphics2D sg2d, OSXSurfaceData surfaceData, BufferedImage srcPixels, Rectangle2D compositingBounds) {356// Thread.dumpStack();357// System.err.println("composite");358// System.err.println(" compositingBounds="+compositingBounds);359int x = (int) compositingBounds.getX();360int y = (int) compositingBounds.getY();361int w = (int) compositingBounds.getWidth();362int h = (int) compositingBounds.getHeight();363364boolean succeded = false;365366Composite composite = sg2d.getComposite();367if (composite instanceof XORComposite) {368// 1st native XOR try369// we try to perform XOR using surface pixels directly370try {371succeded = surfaceData.xorSurfacePixels(sg2d, srcPixels, x, y, w, h, ((XORComposite) composite).getXorColor().getRGB());372} catch (Exception e) {373succeded = false;374}375}376377if (succeded == false) {378// create image with the original pixels of surface379BufferedImage dstInPixels = getSurfacePixels(sg2d, surfaceData, x, y, w, h);380BufferedImage dstOutPixels = null;381382if (composite instanceof XORComposite) {383// 2nd native XOR try384// we try to perform XOR on image's pixels (which were copied from surface first)385try {386OSXSurfaceData osxsd = (OSXSurfaceData) (BufImgSurfaceData.createData(dstInPixels));387succeded = osxsd.xorSurfacePixels(sg2d, srcPixels, 0, 0, w, h, ((XORComposite) composite).getXorColor().getRGB());388dstOutPixels = dstInPixels;389} catch (Exception e) {390succeded = false;391}392}393394// either 2nd native XOR failed OR we have a case of custom compositing395if (succeded == false) {396// create an image into which we'll composite result: we MUST use a different destination (compositing397// is NOT "in place" operation)398dstOutPixels = surfaceData.getCompositingDstOutImage(w, h);399400// prepare rasters for compositing401WritableRaster srcRaster = srcPixels.getRaster();402WritableRaster dstInRaster = dstInPixels.getRaster();403WritableRaster dstOutRaster = dstOutPixels.getRaster();404405CompositeContext compositeContext = composite.createContext(srcPixels.getColorModel(), dstOutPixels.getColorModel(), sg2d.getRenderingHints());406compositeContext.compose(srcRaster, dstInRaster, dstOutRaster);407compositeContext.dispose();408409// gznote: radar bug number410// "cut out" the shape we're interested in411// applyMask(BufImgSurfaceData.createData(dstOutPixels), BufImgSurfaceData.createData(srcPixels), w, h);412}413414// blit the results back to the dst surface415Composite savedComposite = sg2d.getComposite();416AffineTransform savedTM = sg2d.getTransform();417int savedCX = sg2d.constrainX;418int savedCY = sg2d.constrainY;419{420sg2d.setComposite(AlphaComposite.SrcOver);421// all the compositing is done in the coordinate space of the component. the x and the y are the422// position of that component in the surface423// so we need to set the sg2d.transform to identity and we must set the contrainX/Y to 0 for the424// setTransform() to not be constrained425sg2d.constrainX = 0;426sg2d.constrainY = 0;427sg2d.setTransform(sIdentityMatrix);428sg2d.drawImage(dstOutPixels, x, y, x + w, y + h, 0, 0, w, h, null);429}430sg2d.constrainX = savedCX;431sg2d.constrainY = savedCY;432sg2d.setTransform(savedTM);433sg2d.setComposite(savedComposite);434}435}436}437438439