OpenJDK / macosx-port / macosx-port / jdk
changeset 4772:ee9c8c9b5c2e
Printing support : http://java.net/jira/browse/MACOSX_PORT-34
line wrap: on
line diff
--- a/make/sun/lwawt/FILES_c_macosx.gmk Fri Sep 23 21:13:13 2011 +0400 +++ b/make/sun/lwawt/FILES_c_macosx.gmk Fri Sep 23 13:42:06 2011 -0700 @@ -71,7 +71,15 @@ LWCToolkit.m \ ThreadUtilities.m \ GeomUtilities.m \ - PropertiesUtilities.m + PropertiesUtilities.m \ + CPrinterJob.m \ + PrintModel.m \ + PrinterSurfaceData.m \ + PrinterView.m \ + QuartzSurfaceData.m \ + QuartzRenderer.m \ + CTextPipe.m \ + ImageSurfaceData.m FILES_c = \ OGLBlitLoops.c \
--- a/make/sun/lwawt/FILES_export_macosx.gmk Fri Sep 23 21:13:13 2011 +0400 +++ b/make/sun/lwawt/FILES_export_macosx.gmk Fri Sep 23 13:42:06 2011 -0700 @@ -38,6 +38,12 @@ sun/awt/image/ImageRepresentation.java \ sun/awt/image/GifImageDecoder.java \ sun/awt/image/NativeLibLoader.java \ + sun/java2d/CRenderer.java \ + sun/java2d/CompositeCRenderer.java \ + sun/java2d/DataBufferNIOInt.java \ + sun/java2d/IntegerNIORaster.java \ + sun/java2d/OSXSurfaceData.java \ + sun/java2d/OSXOffScreenSurfaceData.java \ sun/java2d/loops/Blit.java \ sun/java2d/loops/BlitBg.java \ sun/java2d/loops/ScaledBlit.java \ @@ -101,6 +107,19 @@ sun/lwawt/LWWindowPeer.java \ sun/lwawt/PlatformWindow.java \ sun/lwawt/SelectionClearListener.java \ + sun/lwawt/macosx/CPrinterDevice.java \ + sun/lwawt/macosx/CPrinterDialog.java \ + sun/lwawt/macosx/CPrinterDialogPeer.java \ + sun/lwawt/macosx/CPrinterGraphics.java \ + sun/lwawt/macosx/CPrinterGraphicsConfig.java \ + sun/lwawt/macosx/CPrinterJob.java \ + sun/lwawt/macosx/CPrinterJobDialog.java \ + sun/lwawt/macosx/CPrinterPageDialog.java \ + sun/lwawt/macosx/CPrinterSurfaceData.java \ + sun/lwawt/macosx/CTextPipe.java \ + sun/java2d/CRenderer.java \ + sun/lwawt/macosx/EventDispatchAccess.java \ + sun/lwawt/macosx/NSPrintInfo.java \ sun/lwawt/macosx/CAccessibility.java \ sun/lwawt/macosx/CAccessible.java \ sun/lwawt/macosx/CFRetainedResource.java \ @@ -166,7 +185,7 @@ sun/java2d/opengl/OGLTextRenderer.java \ sun/java2d/opengl/CGLGraphicsConfig.java \ sun/java2d/opengl/CGLSurfaceData.java \ - sun/awt/ExtendedKeyCodes.java + sun/awt/ExtendedKeyCodes.java FILES_export2 = \ java/awt/AlphaComposite.java \ @@ -244,4 +263,8 @@ java/awt/event/NativeLibLoader.java \ java/awt/peer/ComponentPeer.java \ java/awt/dnd/DnDConstants.java \ - sun/awt/CausedFocusEvent.java + sun/awt/CausedFocusEvent.java \ + java/awt/print/PageFormat.java \ + java/awt/print/Pageable.java \ + java/awt/print/Printable.java \ + java/awt/BasicStroke.java
--- a/src/macosx/classes/sun/awt/CGraphicsEnvironment.java Fri Sep 23 21:13:13 2011 +0400 +++ b/src/macosx/classes/sun/awt/CGraphicsEnvironment.java Fri Sep 23 13:42:06 2011 -0700 @@ -26,6 +26,7 @@ package sun.awt; import java.awt.*; +import java.awt.print.*; import java.util.*; import sun.java2d.*; @@ -191,4 +192,5 @@ } return allFontsWithLogical; } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/java2d/CRenderer.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2011, 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 sun.java2d; + +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.nio.*; + +import sun.awt.image.*; +import sun.java2d.loops.*; +import sun.java2d.pipe.*; +import sun.lwawt.macosx.*; + +public class CRenderer implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe, DrawImagePipe { + native static void init(); + + // cache of the runtime options + static { + init(); // initialize coordinate tables for shapes + } + + native void doLine(SurfaceData sData, float x1, float y1, float x2, float y2); + + public void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) { + drawLine(sg2d, (float) x1, (float) y1, (float) x2, (float) y2); + } + + Line2D lineToShape; + + public void drawLine(SunGraphics2D sg2d, float x1, float y1, float x2, float y2) { + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + if ((sg2d.strokeState != SunGraphics2D.STROKE_CUSTOM) && (OSXSurfaceData.IsSimpleColor(sg2d.paint))) { + surfaceData.doLine(this, sg2d, x1, y1, x2, y2); + } else { + if (lineToShape == null) { + synchronized (this) { + if (lineToShape == null) { + lineToShape = new Line2D.Float(); + } + } + } + synchronized (lineToShape) { + lineToShape.setLine(x1, y1, x2, y2); + drawfillShape(sg2d, sg2d.stroke.createStrokedShape(lineToShape), true, true); + } + } + } + + native void doRect(SurfaceData sData, float x, float y, float width, float height, boolean isfill); + + public void drawRect(SunGraphics2D sg2d, int x, int y, int width, int height) { + drawRect(sg2d, (float) x, (float) y, (float) width, (float) height); + } + + Rectangle2D rectToShape; + + public void drawRect(SunGraphics2D sg2d, float x, float y, float width, float height) { + if ((width < 0) || (height < 0)) return; + + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + if ((sg2d.strokeState != SunGraphics2D.STROKE_CUSTOM) && (OSXSurfaceData.IsSimpleColor(sg2d.paint))) { + surfaceData.doRect(this, sg2d, x, y, width, height, false); + } else { + if (rectToShape == null) { + synchronized (this) { + if (rectToShape == null) { + rectToShape = new Rectangle2D.Float(); + } + } + } + synchronized (rectToShape) { + rectToShape.setRect(x, y, width, height); + drawfillShape(sg2d, sg2d.stroke.createStrokedShape(rectToShape), true, true); + } + } + } + + public void fillRect(SunGraphics2D sg2d, int x, int y, int width, int height) { + fillRect(sg2d, (float) x, (float) y, (float) width, (float) height); + } + + public void fillRect(SunGraphics2D sg2d, float x, float y, float width, float height) { + if ((width >= 0) && (height >= 0)) { + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + surfaceData.doRect(this, sg2d, x, y, width, height, true); + } + } + + native void doRoundRect(SurfaceData sData, float x, float y, float width, float height, float arcW, float arcH, boolean isfill); + + public void drawRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) { + drawRoundRect(sg2d, (float) x, (float) y, (float) width, (float) height, (float) arcWidth, (float) arcHeight); + } + + RoundRectangle2D roundrectToShape; + + public void drawRoundRect(SunGraphics2D sg2d, float x, float y, float width, float height, float arcWidth, float arcHeight) { + if ((width < 0) || (height < 0)) return; + + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + if ((sg2d.strokeState != SunGraphics2D.STROKE_CUSTOM) && (OSXSurfaceData.IsSimpleColor(sg2d.paint))) { + surfaceData.doRoundRect(this, sg2d, x, y, width, height, arcWidth, arcHeight, false); + } else { + if (roundrectToShape == null) { + synchronized (this) { + if (roundrectToShape == null) { + roundrectToShape = new RoundRectangle2D.Float(); + } + } + } + synchronized (roundrectToShape) { + roundrectToShape.setRoundRect(x, y, width, height, arcWidth, arcHeight); + drawfillShape(sg2d, sg2d.stroke.createStrokedShape(roundrectToShape), true, true); + } + } + } + + public void fillRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) { + fillRoundRect(sg2d, (float) x, (float) y, (float) width, (float) height, (float) arcWidth, (float) arcHeight); + } + + public void fillRoundRect(SunGraphics2D sg2d, float x, float y, float width, float height, float arcWidth, float arcHeight) { + if ((width < 0) || (height < 0)) return; + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + surfaceData.doRoundRect(this, sg2d, x, y, width, height, arcWidth, arcHeight, true); + } + + native void doOval(SurfaceData sData, float x, float y, float width, float height, boolean isfill); + + public void drawOval(SunGraphics2D sg2d, int x, int y, int width, int height) { + drawOval(sg2d, (float) x, (float) y, (float) width, (float) height); + } + + Ellipse2D ovalToShape; + + public void drawOval(SunGraphics2D sg2d, float x, float y, float width, float height) { + if ((width < 0) || (height < 0)) return; + + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + if ((sg2d.strokeState != SunGraphics2D.STROKE_CUSTOM) && (OSXSurfaceData.IsSimpleColor(sg2d.paint))) { + surfaceData.doOval(this, sg2d, x, y, width, height, false); + } else { + if (ovalToShape == null) { + synchronized (this) { + if (ovalToShape == null) { + ovalToShape = new Ellipse2D.Float(); + } + } + } + synchronized (ovalToShape) { + ovalToShape.setFrame(x, y, width, height); + drawfillShape(sg2d, sg2d.stroke.createStrokedShape(ovalToShape), true, true); + } + } + } + + public void fillOval(SunGraphics2D sg2d, int x, int y, int width, int height) { + fillOval(sg2d, (float) x, (float) y, (float) width, (float) height); + } + + public void fillOval(SunGraphics2D sg2d, float x, float y, float width, float height) { + if ((width < 0) || (height < 0)) return; + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + surfaceData.doOval(this, sg2d, x, y, width, height, true); + } + + native void doArc(SurfaceData sData, float x, float y, float width, float height, float angleStart, float angleExtent, int type, boolean isfill); + + public void drawArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) { + drawArc(sg2d, x, y, width, height, startAngle, arcAngle, Arc2D.OPEN); + } + + Arc2D arcToShape; + + public void drawArc(SunGraphics2D sg2d, float x, float y, float width, float height, float startAngle, float arcAngle, int type) { + if ((width < 0) || (height < 0)) return; + + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + if ((sg2d.strokeState != SunGraphics2D.STROKE_CUSTOM) && (OSXSurfaceData.IsSimpleColor(sg2d.paint))) { + surfaceData.doArc(this, sg2d, x, y, width, height, startAngle, arcAngle, type, false); + } else { + if (arcToShape == null) { + synchronized (this) { + if (arcToShape == null) { + arcToShape = new Arc2D.Float(); + } + } + } + synchronized (arcToShape) { + arcToShape.setArc(x, y, width, height, startAngle, arcAngle, type); + drawfillShape(sg2d, sg2d.stroke.createStrokedShape(arcToShape), true, true); + } + } + } + + public void fillArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) { + fillArc(sg2d, x, y, width, height, startAngle, arcAngle, Arc2D.PIE); + } + + public void fillArc(SunGraphics2D sg2d, float x, float y, float width, float height, float startAngle, float arcAngle, int type) { + if ((width < 0) || (height < 0)) return; + + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + surfaceData.doArc(this, sg2d, x, y, width, height, startAngle, arcAngle, type, true); + } + + native void doPoly(SurfaceData sData, int[] xpoints, int[] ypoints, int npoints, boolean ispolygon, boolean isfill); + + public void drawPolyline(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints) { + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + if ((sg2d.strokeState != SunGraphics2D.STROKE_CUSTOM) && (OSXSurfaceData.IsSimpleColor(sg2d.paint))) { + surfaceData.doPolygon(this, sg2d, xpoints, ypoints, npoints, false, false); + } else { + GeneralPath polyToShape = new GeneralPath(); + polyToShape.moveTo(xpoints[0], ypoints[0]); + for (int i = 1; i < npoints; i++) { + polyToShape.lineTo(xpoints[i], ypoints[i]); + } + drawfillShape(sg2d, sg2d.stroke.createStrokedShape(polyToShape), true, true); + } + } + + public void drawPolygon(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints) { + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + if ((sg2d.strokeState != SunGraphics2D.STROKE_CUSTOM) && (OSXSurfaceData.IsSimpleColor(sg2d.paint))) { + surfaceData.doPolygon(this, sg2d, xpoints, ypoints, npoints, true, false); + } else { + GeneralPath polyToShape = new GeneralPath(); + polyToShape.moveTo(xpoints[0], ypoints[0]); + for (int i = 1; i < npoints; i++) { + polyToShape.lineTo(xpoints[i], ypoints[i]); + } + polyToShape.lineTo(xpoints[0], ypoints[0]); + drawfillShape(sg2d, sg2d.stroke.createStrokedShape(polyToShape), true, true); + } + } + + public void fillPolygon(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints) { + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + surfaceData.doPolygon(this, sg2d, xpoints, ypoints, npoints, true, true); + } + + native void doShape(SurfaceData sData, int length, FloatBuffer coordinates, IntBuffer types, int windingRule, boolean isfill, boolean shouldApplyOffset); + + void drawfillShape(SunGraphics2D sg2d, Shape s, boolean isfill, boolean shouldApplyOffset) { + if (s == null) { throw new NullPointerException(); } + + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + // TODO: + boolean sOptimizeShapes = true; + if (sOptimizeShapes && OSXSurfaceData.IsSimpleColor(sg2d.paint)) { + if (s instanceof Rectangle2D) { + Rectangle2D rectangle = (Rectangle2D) s; + + float x = (float) rectangle.getX(); + float y = (float) rectangle.getY(); + float w = (float) rectangle.getWidth(); + float h = (float) rectangle.getHeight(); + if (isfill) { + fillRect(sg2d, x, y, w, h); + } else { + drawRect(sg2d, x, y, w, h); + } + } else if (s instanceof Ellipse2D) { + Ellipse2D ellipse = (Ellipse2D) s; + + float x = (float) ellipse.getX(); + float y = (float) ellipse.getY(); + float w = (float) ellipse.getWidth(); + float h = (float) ellipse.getHeight(); + + if (isfill) { + fillOval(sg2d, x, y, w, h); + } else { + drawOval(sg2d, x, y, w, h); + } + } else if (s instanceof Arc2D) { + Arc2D arc = (Arc2D) s; + + float x = (float) arc.getX(); + float y = (float) arc.getY(); + float w = (float) arc.getWidth(); + float h = (float) arc.getHeight(); + float as = (float) arc.getAngleStart(); + float ae = (float) arc.getAngleExtent(); + + if (isfill) { + fillArc(sg2d, x, y, w, h, as, ae, arc.getArcType()); + } else { + drawArc(sg2d, x, y, w, h, as, ae, arc.getArcType()); + } + } else if (s instanceof RoundRectangle2D) { + RoundRectangle2D roundrect = (RoundRectangle2D) s; + + float x = (float) roundrect.getX(); + float y = (float) roundrect.getY(); + float w = (float) roundrect.getWidth(); + float h = (float) roundrect.getHeight(); + float aw = (float) roundrect.getArcWidth(); + float ah = (float) roundrect.getArcHeight(); + + if (isfill) { + fillRoundRect(sg2d, x, y, w, h, aw, ah); + } else { + drawRoundRect(sg2d, x, y, w, h, aw, ah); + } + } else if (s instanceof Line2D) { + Line2D line = (Line2D) s; + + float x1 = (float) line.getX1(); + float y1 = (float) line.getY1(); + float x2 = (float) line.getX2(); + float y2 = (float) line.getY2(); + + drawLine(sg2d, x1, y1, x2, y2); + } else if (s instanceof Point2D) { + Point2D point = (Point2D) s; + + float x = (float) point.getX(); + float y = (float) point.getY(); + + drawLine(sg2d, x, y, x, y); + } else { + GeneralPath gp; + + if (s instanceof GeneralPath) { + gp = (GeneralPath) s; + } else { + gp = new GeneralPath(s); + } + + PathIterator pi = gp.getPathIterator(null); + if (pi.isDone() == false) { + surfaceData.drawfillShape(this, sg2d, gp, isfill, shouldApplyOffset); + } + } + } else { + GeneralPath gp; + + if (s instanceof GeneralPath) { + gp = (GeneralPath) s; + } else { + gp = new GeneralPath(s); + } + + PathIterator pi = gp.getPathIterator(null); + if (pi.isDone() == false) { + surfaceData.drawfillShape(this, sg2d, gp, isfill, shouldApplyOffset); + } + } + } + + public void draw(SunGraphics2D sg2d, Shape s) { + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + if ((sg2d.strokeState != SunGraphics2D.STROKE_CUSTOM) && (OSXSurfaceData.IsSimpleColor(sg2d.paint))) { + drawfillShape(sg2d, s, false, true); + } else { + drawfillShape(sg2d, sg2d.stroke.createStrokedShape(s), true, true); + } + } + + public void fill(SunGraphics2D sg2d, Shape s) { + drawfillShape(sg2d, s, true, false); + } + + native void doImage(SurfaceData sData, SurfaceData img, boolean fliph, boolean flipv, int w, int h, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh); + + // Copy img to scaled sg2d @ x,y with width height + public boolean scaleImage(SunGraphics2D sg2d, Image img, int x, int y, int width, int height, Color bgColor) { + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + + int sx = 0; + int sy = 0; + int iw = img.getWidth(null); + int ih = img.getHeight(null); + + return scaleImage(sg2d, img, x, y, x + width, y + height, sx, sy, sx + iw, sy + ih, bgColor); + } + + // Copy img, clipped to sx1, sy1 by sx2, sy2 to dx1, dy2 by dx2, dy2 + public boolean scaleImage(SunGraphics2D sg2d, Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgColor) { + + // System.err.println("scaleImage"); + // System.err.println(" sx1="+sx1+", sy1="+sy1+", sx2="+sx2+", sy2="+sy2); + // System.err.println(" dx1="+dx1+", dy1="+dy1+", dx2="+dx2+", dy2="+dy2); + + int srcW, srcH, dstW, dstH; + int srcX, srcY, dstX, dstY; + boolean srcWidthFlip = false; + boolean srcHeightFlip = false; + boolean dstWidthFlip = false; + boolean dstHeightFlip = false; + + if (sx2 > sx1) { + srcW = sx2 - sx1; + srcX = sx1; + } else { + srcWidthFlip = true; + srcW = sx1 - sx2; + srcX = sx2; + } + if (sy2 > sy1) { + srcH = sy2 - sy1; + srcY = sy1; + } else { + srcHeightFlip = true; + srcH = sy1 - sy2; + srcY = sy2; + } + if (dx2 > dx1) { + dstW = dx2 - dx1; + dstX = dx1; + } else { + dstW = dx1 - dx2; + dstWidthFlip = true; + dstX = dx2; + } + if (dy2 > dy1) { + dstH = dy2 - dy1; + dstY = dy1; + } else { + dstH = dy1 - dy2; + dstHeightFlip = true; + dstY = dy2; + } + if (srcW <= 0 || srcH <= 0) { return true; } + + boolean flipv = (srcHeightFlip != dstHeightFlip); + boolean fliph = (srcWidthFlip != dstWidthFlip); + + return blitImage(sg2d, img, fliph, flipv, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH, bgColor); + } + + 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) { + CPrinterSurfaceData surfaceData = (CPrinterSurfaceData)sg2d.getSurfaceData(); + OSXOffScreenSurfaceData imgSurfaceData = (OSXOffScreenSurfaceData) OSXOffScreenSurfaceData.createNewSurface((BufferedImage)img); + surfaceData.blitImage(this, sg2d, imgSurfaceData, fliph, flipv, sx, sy, sw, sh, dx, dy, dw, dh, bgColor); + return true; + } + + // Copy img to sg2d @ x, y + protected boolean copyImage(SunGraphics2D sg2d, Image img, int dx, int dy, Color bgColor) { + if (img == null) { return true; } + + int sx = 0; + int sy = 0; + int width = img.getWidth(null); + int height = img.getHeight(null); + + return blitImage(sg2d, img, false, false, sx, sy, width, height, dx, dy, width, height, bgColor); + } + + // Copy img, clipped to sx, sy with width, height to sg2d @ dx, dy + protected boolean copyImage(SunGraphics2D sg2d, Image img, int dx, int dy, int sx, int sy, int width, int height, Color bgColor) { + return blitImage(sg2d, img, false, false, sx, sy, width, height, dx, dy, width, height, bgColor); + } + + protected void transformImage(SunGraphics2D sg2d, Image img, int x, int y, BufferedImageOp op, AffineTransform xf, Color bgColor) { + if (img != null) { + int iw = img.getWidth(null); + int ih = img.getHeight(null); + + if ((op != null) && (img instanceof BufferedImage)) { + if (((BufferedImage) img).getType() == BufferedImage.TYPE_CUSTOM) { + // BufferedImageOp can not handle custom images + BufferedImage dest = null; + dest = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = dest.createGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + img = op.filter(dest, null); + } else { + // sun.awt.image.BufImgSurfaceData.createData((BufferedImage)img).finishLazyDrawing(); + img = op.filter((BufferedImage) img, null); + } + + iw = img.getWidth(null); + ih = img.getHeight(null); + } + + if (xf != null) { + AffineTransform reset = sg2d.getTransform(); + sg2d.transform(xf); + scaleImage(sg2d, img, x, y, x + iw, y + ih, 0, 0, iw, ih, bgColor); + sg2d.setTransform(reset); + } else { + scaleImage(sg2d, img, x, y, x + iw, y + ih, 0, 0, iw, ih, bgColor); + } + } else { + throw new NullPointerException(); + } + } + + // copied from DrawImage.java + protected boolean imageReady(sun.awt.image.ToolkitImage sunimg, ImageObserver observer) { + if (sunimg.hasError()) { + if (observer != null) { + observer.imageUpdate(sunimg, ImageObserver.ERROR | ImageObserver.ABORT, -1, -1, -1, -1); + } + return false; + } + return true; + } + + // copied from DrawImage.java + public boolean copyImage(SunGraphics2D sg2d, Image img, int x, int y, Color bgColor, ImageObserver observer) { + if (img == null) { throw new NullPointerException(); } + + if (!(img instanceof sun.awt.image.ToolkitImage)) { return copyImage(sg2d, img, x, y, bgColor); } + + sun.awt.image.ToolkitImage sunimg = (sun.awt.image.ToolkitImage) img; + if (!imageReady(sunimg, observer)) { return false; } + ImageRepresentation ir = sunimg.getImageRep(); + return ir.drawToBufImage(sg2d, sunimg, x, y, bgColor, observer); + } + + // copied from DrawImage.java + public boolean copyImage(SunGraphics2D sg2d, Image img, int dx, int dy, int sx, int sy, int width, int height, Color bgColor, ImageObserver observer) { + if (img == null) { throw new NullPointerException(); } + + if (!(img instanceof sun.awt.image.ToolkitImage)) { return copyImage(sg2d, img, dx, dy, sx, sy, width, height, bgColor); } + + sun.awt.image.ToolkitImage sunimg = (sun.awt.image.ToolkitImage) img; + if (!imageReady(sunimg, observer)) { return false; } + ImageRepresentation ir = sunimg.getImageRep(); + return ir.drawToBufImage(sg2d, sunimg, dx, dy, (dx + width), (dy + height), sx, sy, (sx + width), (sy + height), null, observer); + } + + // copied from DrawImage.java + public boolean scaleImage(SunGraphics2D sg2d, Image img, int x, int y, int width, int height, Color bgColor, ImageObserver observer) { + if (img == null) { throw new NullPointerException(); } + + if (!(img instanceof sun.awt.image.ToolkitImage)) { return scaleImage(sg2d, img, x, y, width, height, bgColor); } + + sun.awt.image.ToolkitImage sunimg = (sun.awt.image.ToolkitImage) img; + if (!imageReady(sunimg, observer)) { return false; } + ImageRepresentation ir = sunimg.getImageRep(); + return ir.drawToBufImage(sg2d, sunimg, x, y, width, height, bgColor, observer); + } + + // copied from DrawImage.java + public boolean scaleImage(SunGraphics2D sg2d, Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgColor, ImageObserver observer) { + if (img == null) { throw new NullPointerException(); } + + if (!(img instanceof sun.awt.image.ToolkitImage)) { return scaleImage(sg2d, img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgColor); } + + sun.awt.image.ToolkitImage sunimg = (sun.awt.image.ToolkitImage) img; + if (!imageReady(sunimg, observer)) { return false; } + ImageRepresentation ir = sunimg.getImageRep(); + return ir.drawToBufImage(sg2d, sunimg, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgColor, observer); + } + + // copied from DrawImage.java + public boolean transformImage(SunGraphics2D sg2d, Image img, AffineTransform atfm, ImageObserver observer) { + if (img == null) { throw new NullPointerException(); } + + if (!(img instanceof sun.awt.image.ToolkitImage)) { + transformImage(sg2d, img, 0, 0, null, atfm, null); + return true; + } + + sun.awt.image.ToolkitImage sunimg = (sun.awt.image.ToolkitImage) img; + if (!imageReady(sunimg, observer)) { return false; } + ImageRepresentation ir = sunimg.getImageRep(); + return ir.drawToBufImage(sg2d, sunimg, atfm, observer); + } + + // copied from DrawImage.java + public void transformImage(SunGraphics2D sg2d, BufferedImage img, BufferedImageOp op, int x, int y) { + if (img != null) { + transformImage(sg2d, img, x, y, op, null, null); + } else { + throw new NullPointerException(); + } + } + + public CRenderer traceWrap() { + return new Tracer(); + } + + public static class Tracer extends CRenderer { + void doLine(SurfaceData sData, float x1, float y1, float x2, float y2) { + GraphicsPrimitive.tracePrimitive("QuartzLine"); + super.doLine(sData, x1, y1, x2, y2); + } + + void doRect(SurfaceData sData, float x, float y, float width, float height, boolean isfill) { + GraphicsPrimitive.tracePrimitive("QuartzRect"); + super.doRect(sData, x, y, width, height, isfill); + } + + void doRoundRect(SurfaceData sData, float x, float y, float width, float height, float arcW, float arcH, boolean isfill) { + GraphicsPrimitive.tracePrimitive("QuartzRoundRect"); + super.doRoundRect(sData, x, y, width, height, arcW, arcH, isfill); + } + + void doOval(SurfaceData sData, float x, float y, float width, float height, boolean isfill) { + GraphicsPrimitive.tracePrimitive("QuartzOval"); + super.doOval(sData, x, y, width, height, isfill); + } + + void doArc(SurfaceData sData, float x, float y, float width, float height, float angleStart, float angleExtent, int type, boolean isfill) { + GraphicsPrimitive.tracePrimitive("QuartzArc"); + super.doArc(sData, x, y, width, height, angleStart, angleExtent, type, isfill); + } + + void doPoly(SurfaceData sData, int[] xpoints, int[] ypoints, int npoints, boolean ispolygon, boolean isfill) { + GraphicsPrimitive.tracePrimitive("QuartzDoPoly"); + super.doPoly(sData, xpoints, ypoints, npoints, ispolygon, isfill); + } + + void doShape(SurfaceData sData, int length, FloatBuffer coordinates, IntBuffer types, int windingRule, boolean isfill, boolean shouldApplyOffset) { + GraphicsPrimitive.tracePrimitive("QuartzFillOrDrawShape"); + super.doShape(sData, length, coordinates, types, windingRule, isfill, shouldApplyOffset); + } + + void doImage(SurfaceData sData, SurfaceData img, boolean fliph, boolean flipv, int w, int h, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh) { + GraphicsPrimitive.tracePrimitive("QuartzDrawImage"); + super.doImage(sData, img, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/java2d/CompositeCRenderer.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2011, 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 sun.java2d; + +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.awt.image.*; + +import sun.awt.image.*; +import sun.java2d.loops.*; +import sun.java2d.pipe.*; + +public class CompositeCRenderer extends CRenderer implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe, DrawImagePipe, TextPipe { + final static int fPadding = 4; + final static int fPaddingHalf = fPadding / 2; + + private static AffineTransform sIdentityMatrix = new AffineTransform(); + + AffineTransform ShapeTM = new AffineTransform(); + Rectangle2D ShapeBounds = new Rectangle2D.Float(); + + Line2D line = new Line2D.Float(); + Rectangle2D rectangle = new Rectangle2D.Float(); + RoundRectangle2D roundrectangle = new RoundRectangle2D.Float(); + Ellipse2D ellipse = new Ellipse2D.Float(); + Arc2D arc = new Arc2D.Float(); + + public synchronized void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) { + // create shape corresponding to this primitive + line.setLine(x1, y1, x2, y2); + + draw(sg2d, line); + } + + public synchronized void drawRect(SunGraphics2D sg2d, int x, int y, int width, int height) { + // create shape corresponding to this primitive + rectangle.setRect(x, y, width, height); + + draw(sg2d, rectangle); + } + + public synchronized void drawRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) { + // create shape corresponding to this primitive + roundrectangle.setRoundRect(x, y, width, height, arcWidth, arcHeight); + + draw(sg2d, roundrectangle); + } + + public synchronized void drawOval(SunGraphics2D sg2d, int x, int y, int width, int height) { + // create shape corresponding to this primitive + ellipse.setFrame(x, y, width, height); + + draw(sg2d, ellipse); + } + + public synchronized void drawArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) { + // create shape corresponding to this primitive + arc.setArc(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN); + + draw(sg2d, arc); + } + + public synchronized void drawPolyline(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints) { + doPolygon(sg2d, xpoints, ypoints, npoints, false, false); + } + + public synchronized void drawPolygon(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints) { + doPolygon(sg2d, xpoints, ypoints, npoints, true, false); + } + + public synchronized void fillRect(SunGraphics2D sg2d, int x, int y, int width, int height) { + // create shape corresponding to this primitive + rectangle.setRect(x, y, width, height); + + fill(sg2d, rectangle); + } + + public synchronized void fillRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) { + // create shape corresponding to this primitive + roundrectangle.setRoundRect(x, y, width, height, arcWidth, arcHeight); + + fill(sg2d, roundrectangle); + } + + public synchronized void fillOval(SunGraphics2D sg2d, int x, int y, int width, int height) { + // create shape corresponding to this primitive + ellipse.setFrame(x, y, width, height); + + fill(sg2d, ellipse); + } + + public synchronized void fillArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) { + // create shape corresponding to this primitive + arc.setArc(x, y, width, height, startAngle, arcAngle, Arc2D.PIE); + + fill(sg2d, arc); + } + + public synchronized void fillPolygon(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints) { + doPolygon(sg2d, xpoints, ypoints, npoints, true, true); + } + + public synchronized void doPolygon(SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints, boolean ispolygon, boolean isfill) { + GeneralPath gp = new GeneralPath(Path2D.WIND_NON_ZERO, npoints); + gp.moveTo(xpoints[0], ypoints[0]); + for (int i = 1; i < npoints; i++) { + gp.lineTo(xpoints[i], ypoints[i]); + } + if (ispolygon) { + // according to the specs (only applies to polygons, not polylines) + if ((xpoints[0] != xpoints[npoints - 1]) || (ypoints[0] != ypoints[npoints - 1])) { + gp.lineTo(xpoints[0], ypoints[0]); + } + } + + doShape(sg2d, (OSXSurfaceData) sg2d.getSurfaceData(), (Shape) gp, isfill); + } + + public synchronized void draw(SunGraphics2D sg2d, Shape shape) { + doShape(sg2d, (OSXSurfaceData) sg2d.getSurfaceData(), shape, false); + } + + public synchronized void fill(SunGraphics2D sg2d, Shape shape) { + doShape(sg2d, (OSXSurfaceData) sg2d.getSurfaceData(), shape, true); + } + + void doShape(SunGraphics2D sg2d, OSXSurfaceData surfaceData, Shape shape, boolean isfill) { + Rectangle2D shapeBounds = shape.getBounds2D(); + + // We don't want to draw with negative width and height (CRender doesn't do it and Windows doesn't do it either) + // Drawing with negative w and h, can cause CG problems down the line <rdar://3960579> (vm) + if ((shapeBounds.getWidth() < 0) || (shapeBounds.getHeight() < 0)) { return; } + + // get final destination compositing bounds (after all transformations if needed) + Rectangle2D compositingBounds = padBounds(sg2d, shape); + + // constrain the bounds to be within surface bounds + clipBounds(sg2d, compositingBounds); + + // if the compositing region is empty we skip all remaining compositing work: + if (compositingBounds.isEmpty() == false) { + BufferedImage srcPixels; + // create a matching surface into which we'll render the primitive to be composited + // with the desired dimension + srcPixels = surfaceData.getCompositingSrcImage((int) (compositingBounds.getWidth()), + (int) (compositingBounds.getHeight())); + + Graphics2D g = srcPixels.createGraphics(); + + // sync up graphics state + ShapeTM.setToTranslation(-compositingBounds.getX(), -compositingBounds.getY()); + ShapeTM.concatenate(sg2d.transform); + g.setTransform(ShapeTM); + g.setRenderingHints(sg2d.getRenderingHints()); + g.setPaint(sg2d.getPaint()); + g.setStroke(sg2d.getStroke()); + + // render the primitive to be composited + if (isfill) { + g.fill(shape); + } else { + g.draw(shape); + } + + g.dispose(); + + composite(sg2d, surfaceData, srcPixels, compositingBounds); + } + } + + public synchronized void drawString(SunGraphics2D sg2d, String str, double x, double y) { + drawGlyphVector(sg2d, sg2d.getFont().createGlyphVector(sg2d.getFontRenderContext(), str), x, y); + } + + public synchronized void drawChars(SunGraphics2D sg2d, char data[], int offset, int length, int x, int y) { + drawString(sg2d, new String(data, offset, length), x, y); + } + + public synchronized void drawGlyphVector(SunGraphics2D sg2d, GlyphVector glyphVector, double x, double y) { + drawGlyphVector(sg2d, glyphVector, (float) x, (float) y); + } + + public synchronized void drawGlyphVector(SunGraphics2D sg2d, GlyphVector glyphVector, float x, float y) { + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + + Shape shape = glyphVector.getOutline(x, y); + + // get final destination compositing bounds (after all transformations if needed) + Rectangle2D compositingBounds = padBounds(sg2d, shape); + + // constrain the bounds to be within surface bounds + clipBounds(sg2d, compositingBounds); + + // if the compositing region is empty we skip all remaining compositing work: + if (compositingBounds.isEmpty() == false) { + BufferedImage srcPixels; + { + // create matching image into which we'll render the primitive to be composited + srcPixels = surfaceData.getCompositingSrcImage((int) compositingBounds.getWidth(), (int) compositingBounds.getHeight()); + + Graphics2D g = srcPixels.createGraphics(); + + // sync up graphics state + ShapeTM.setToTranslation(-compositingBounds.getX(), -compositingBounds.getY()); + ShapeTM.concatenate(sg2d.transform); + g.setTransform(ShapeTM); + g.setPaint(sg2d.getPaint()); + g.setStroke(sg2d.getStroke()); + g.setFont(sg2d.getFont()); + g.setRenderingHints(sg2d.getRenderingHints()); + + // render the primitive to be composited + g.drawGlyphVector(glyphVector, x, y); + g.dispose(); + } + + composite(sg2d, surfaceData, srcPixels, compositingBounds); + } + } + + 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) { + OSXSurfaceData surfaceData = (OSXSurfaceData) sg2d.getSurfaceData(); + + // get final destination compositing bounds (after all transformations if needed) + dx = (flipv == false) ? dx : dx - dw; + dy = (fliph == false) ? dy : dy - dh; + ShapeBounds.setFrame(dx, dy, dw, dh); + Rectangle2D compositingBounds = ShapeBounds; + boolean complexTransform = (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE); + if (complexTransform == false) { + double newX = Math.floor(compositingBounds.getX() + sg2d.transX); + double newY = Math.floor(compositingBounds.getY() + sg2d.transY); + double newW = Math.ceil(compositingBounds.getWidth()) + (newX < compositingBounds.getX() ? 1 : 0); + double newH = Math.ceil(compositingBounds.getHeight()) + (newY < compositingBounds.getY() ? 1 : 0); + compositingBounds.setRect(newX, newY, newW, newH); + } else { + Shape transformedShape = sg2d.transform.createTransformedShape(compositingBounds); + compositingBounds = transformedShape.getBounds2D(); + double newX = Math.floor(compositingBounds.getX()); + double newY = Math.floor(compositingBounds.getY()); + double newW = Math.ceil(compositingBounds.getWidth()) + (newX < compositingBounds.getX() ? 1 : 0); + double newH = Math.ceil(compositingBounds.getHeight()) + (newY < compositingBounds.getY() ? 1 : 0); + compositingBounds.setRect(newX, newY, newW, newH); + } + + // constrain the bounds to be within surface bounds + clipBounds(sg2d, compositingBounds); + + // if the compositing region is empty we skip all remaining compositing work: + if (compositingBounds.isEmpty() == false) { + BufferedImage srcPixels; + { + // create matching image into which we'll render the primitive to be composited + srcPixels = surfaceData.getCompositingSrcImage((int) compositingBounds.getWidth(), (int) compositingBounds.getHeight()); + + Graphics2D g = srcPixels.createGraphics(); + + // sync up graphics state + ShapeTM.setToTranslation(-compositingBounds.getX(), -compositingBounds.getY()); + ShapeTM.concatenate(sg2d.transform); + g.setTransform(ShapeTM); + g.setRenderingHints(sg2d.getRenderingHints()); + g.setComposite(AlphaComposite.Src); + + int sx2 = (flipv == false) ? sx + sw : sx - sw; + int sy2 = (fliph == false) ? sy + sh : sy - sh; + g.drawImage(img, dx, dy, dx + dw, dy + dh, sx, sy, sx2, sy2, null); + + g.dispose(); + } + + composite(sg2d, surfaceData, srcPixels, compositingBounds); + } + + return true; + } + + Rectangle2D padBounds(SunGraphics2D sg2d, Shape shape) { + shape = sg2d.transformShape(shape); + + int paddingHalf = fPaddingHalf; + int padding = fPadding; + if (sg2d.stroke != null) { + if (sg2d.stroke instanceof BasicStroke) { + int width = (int) (((BasicStroke) sg2d.stroke).getLineWidth() + 0.5f); + int widthHalf = width / 2 + 1; + paddingHalf += widthHalf; + padding += 2 * widthHalf; + } else { + shape = sg2d.stroke.createStrokedShape(shape); + } + } + Rectangle2D bounds = shape.getBounds2D(); + bounds.setRect(bounds.getX() - paddingHalf, bounds.getY() - paddingHalf, bounds.getWidth() + padding, bounds.getHeight() + padding); + + double newX = Math.floor(bounds.getX()); + double newY = Math.floor(bounds.getY()); + double newW = Math.ceil(bounds.getWidth()) + (newX < bounds.getX() ? 1 : 0); + double newH = Math.ceil(bounds.getHeight()) + (newY < bounds.getY() ? 1 : 0); + bounds.setRect(newX, newY, newW, newH); + + return bounds; + } + + void clipBounds(SunGraphics2D sg2d, Rectangle2D bounds) { + /* + * System.err.println("clipBounds"); System.err.println(" transform="+sg2d.transform); + * System.err.println(" getTransform()="+sg2d.getTransform()); + * System.err.println(" complexTransform="+(sg2d.transformState > SunGraphics2D.TRANSFORM_TRANSLATESCALE)); + * System.err.println(" transX="+sg2d.transX+" transY="+sg2d.transX); + * System.err.println(" sg2d.constrainClip="+sg2d.constrainClip); if (sg2d.constrainClip != null) { + * System.err + * .println(" constrainClip: x="+sg2d.constrainClip.getLoX()+" y="+sg2d.constrainClip.getLoY()+" w=" + * +sg2d.constrainClip.getWidth()+" h="+sg2d.constrainClip.getHeight());} + * System.err.println(" constrainX="+sg2d.constrainX+" constrainY="+sg2d.constrainY); + * System.err.println(" usrClip="+sg2d.usrClip); + * System.err.println(" devClip: x="+sg2d.devClip.getLoX()+" y=" + * +sg2d.devClip.getLoY()+" w="+sg2d.devClip.getWidth()+" h="+sg2d.devClip.getHeight()); + */ + Region intersection = sg2d.clipRegion.getIntersectionXYWH((int) bounds.getX(), (int) bounds.getY(), (int) bounds.getWidth(), (int) bounds.getHeight()); + bounds.setRect(intersection.getLoX(), intersection.getLoY(), intersection.getWidth(), intersection.getHeight()); + } + + BufferedImage getSurfacePixels(SunGraphics2D sg2d, OSXSurfaceData surfaceData, int x, int y, int w, int h) { + // create an image to copy the surface pixels into + BufferedImage dstInPixels = surfaceData.getCompositingDstInImage(w, h); + + // get the pixels from the dst surface + return surfaceData.copyArea(sg2d, x, y, w, h, dstInPixels); + } + + void composite(SunGraphics2D sg2d, OSXSurfaceData surfaceData, BufferedImage srcPixels, Rectangle2D compositingBounds) { + // Thread.dumpStack(); + // System.err.println("composite"); + // System.err.println(" compositingBounds="+compositingBounds); + int x = (int) compositingBounds.getX(); + int y = (int) compositingBounds.getY(); + int w = (int) compositingBounds.getWidth(); + int h = (int) compositingBounds.getHeight(); + + boolean succeded = false; + + Composite composite = sg2d.getComposite(); + if (composite instanceof XORComposite) { + // 1st native XOR try + // we try to perform XOR using surface pixels directly + try { + succeded = surfaceData.xorSurfacePixels(sg2d, srcPixels, x, y, w, h, ((XORComposite) composite).getXorColor().getRGB()); + } catch (Exception e) { + succeded = false; + } + } + + if (succeded == false) { + // create image with the original pixels of surface + BufferedImage dstInPixels = getSurfacePixels(sg2d, surfaceData, x, y, w, h); + BufferedImage dstOutPixels = null; + + if (composite instanceof XORComposite) { + // 2nd native XOR try + // we try to perform XOR on image's pixels (which were copied from surface first) + try { + OSXSurfaceData osxsd = (OSXSurfaceData) (BufImgSurfaceData.createData(dstInPixels)); + succeded = osxsd.xorSurfacePixels(sg2d, srcPixels, 0, 0, w, h, ((XORComposite) composite).getXorColor().getRGB()); + dstOutPixels = dstInPixels; + } catch (Exception e) { + succeded = false; + } + } + + // either 2nd native XOR failed OR we have a case of custom compositing + if (succeded == false) { + // create an image into which we'll composite result: we MUST use a different destination (compositing + // is NOT "in place" operation) + dstOutPixels = surfaceData.getCompositingDstOutImage(w, h); + + // prepare rasters for compositing + WritableRaster srcRaster = srcPixels.getRaster(); + WritableRaster dstInRaster = dstInPixels.getRaster(); + WritableRaster dstOutRaster = dstOutPixels.getRaster(); + + CompositeContext compositeContext = composite.createContext(srcPixels.getColorModel(), dstOutPixels.getColorModel(), sg2d.getRenderingHints()); + compositeContext.compose(srcRaster, dstInRaster, dstOutRaster); + compositeContext.dispose(); + + // gznote: radar bug number + // "cut out" the shape we're interested in + // applyMask(BufImgSurfaceData.createData(dstOutPixels), BufImgSurfaceData.createData(srcPixels), w, h); + } + + // blit the results back to the dst surface + Composite savedComposite = sg2d.getComposite(); + AffineTransform savedTM = sg2d.getTransform(); + int savedCX = sg2d.constrainX; + int savedCY = sg2d.constrainY; + { + sg2d.setComposite(AlphaComposite.SrcOver); + // all the compositing is done in the coordinate space of the component. the x and the y are the + // position of that component in the surface + // so we need to set the sg2d.transform to identity and we must set the contrainX/Y to 0 for the + // setTransform() to not be constrained + sg2d.constrainX = 0; + sg2d.constrainY = 0; + sg2d.setTransform(sIdentityMatrix); + sg2d.drawImage(dstOutPixels, x, y, x + w, y + h, 0, 0, w, h, null); + } + sg2d.constrainX = savedCX; + sg2d.constrainY = savedCY; + sg2d.setTransform(savedTM); + sg2d.setComposite(savedComposite); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/java2d/DataBufferNIOInt.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2011, 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 sun.java2d; + +import java.awt.image.DataBuffer; +import java.nio.*; + +public final class DataBufferNIOInt extends DataBuffer { + + /** The default data bank. */ + IntBuffer data; + + /** All data banks */ + IntBuffer bankdata[]; + + /** + * Constructs an integer-based <CODE>DataBuffer</CODE> with a single bank + * and the specified size. + * + * @param size The size of the <CODE>DataBuffer</CODE>. + */ + public DataBufferNIOInt(int size) { + super(TYPE_INT,size); + //+++gdb how to get sizeof(int) in java? Using 4 for now. + data = getBufferOfSize(size * 4).asIntBuffer(); + bankdata = new IntBuffer[1]; + bankdata[0] = data; + } + + /** + * Returns the default (first) IntBuffer in <CODE>DataBuffer</CODE>. + * + * @return The first IntBuffer. + */ + public IntBuffer getBuffer() { + return data; + } + + /** + * Returns the Buffer for the specified bank. + * + * @param bank The bank whose Buffer you want to get. + * @return The Buffer for the specified bank. + */ + public IntBuffer getBuffer(int bank) { + return bankdata[bank]; + } + + /** + * Returns the default (first) int data array in <CODE>DataBuffer</CODE>. + * + * @return The first integer data array. + */ + public int[] getData() { + return data.array(); + } + + /** + * Returns the data array for the specified bank. + * + * @param bank The bank whose data array you want to get. + * @return The data array for the specified bank. + */ + public int[] getData(int bank) { + return bankdata[bank].array(); + } + + /** + * Returns the data arrays for all banks. + * @return All of the data arrays. + */ + public int[][] getBankData() { + // Unsupported. + return null; + } + + /** + * Returns the requested data array element from the first (default) bank. + * + * @param i The data array element you want to get. + * @return The requested data array element as an integer. + * @see #setElem(int, int) + * @see #setElem(int, int, int) + */ + public int getElem(int i) { + return data.get(i+offset); + } + + /** + * Returns the requested data array element from the specified bank. + * + * @param bank The bank from which you want to get a data array element. + * @param i The data array element you want to get. + * @return The requested data array element as an integer. + * @see #setElem(int, int) + * @see #setElem(int, int, int) + */ + public int getElem(int bank, int i) { + return bankdata[bank].get(i+offsets[bank]); + } + + /** + * Sets the requested data array element in the first (default) bank + * to the specified value. + * + * @param i The data array element you want to set. + * @param val The integer value to which you want to set the data array element. + * @see #getElem(int) + * @see #getElem(int, int) + */ + public void setElem(int i, int val) { + data.put(i+offset, val); + } + + /** + * Sets the requested data array element in the specified bank + * to the integer value <CODE>i</CODE>. + * @param bank The bank in which you want to set the data array element. + * @param i The data array element you want to set. + * @param val The integer value to which you want to set the specified data array element. + * @see #getElem(int) + * @see #getElem(int, int) + */ + public void setElem(int bank, int i, int val) { + bankdata[bank].put(i+offsets[bank], val); + } + + ByteBuffer getBufferOfSize(int size) + { + ByteBuffer buffer = ByteBuffer.allocateDirect(size); + buffer.order(ByteOrder.nativeOrder()); + return buffer; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/java2d/IntegerNIORaster.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011, 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 sun.java2d; + +import java.awt.*; +import java.awt.image.*; +import java.nio.IntBuffer; +import sun.awt.image.SunWritableRaster; + +public class IntegerNIORaster extends SunWritableRaster { + + protected IntBuffer data; + + public static WritableRaster createNIORaster(int w, int h, int bandMasks[], Point location) { + if ((w <= 0) || (h <= 0)) { + throw new IllegalArgumentException("Width (" + w + ") and height (" + h + + ") cannot be <= 0"); + } + // This is cribbed from java.awt.image.Raster. + DataBuffer db = new DataBufferNIOInt(w * h); + if (location == null) { + location = new Point(0, 0); + } + SinglePixelPackedSampleModel sppsm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, w, bandMasks); + return new IntegerNIORaster(sppsm, db, location); + } + + public IntegerNIORaster(SampleModel sampleModel, DataBuffer dataBuffer, Point origin) { + // This is all cribbed from sun.awt.image.IntegerInterleavedRaster & sun.awt.image.IntegerComponentRaster + super(sampleModel, dataBuffer, new Rectangle(origin.x, origin.y, sampleModel.getWidth(), sampleModel.getHeight()), origin, null); + if (!(dataBuffer instanceof DataBufferNIOInt)) { + throw new RasterFormatException("IntegerNIORasters must have DataBufferNIOInt DataBuffers"); + } + this.data = ((DataBufferNIOInt)dataBuffer).getBuffer(); + } + + public WritableRaster createCompatibleWritableRaster() { + return new IntegerNIORaster(sampleModel, new DataBufferNIOInt(sampleModel.getWidth() * sampleModel.getHeight()), new Point(0,0)); + } + + public WritableRaster createCompatibleWritableRaster(int w, int h) { + if (w <= 0 || h <=0) { + throw new RasterFormatException("negative " + ((w <= 0) ? "width" : "height")); + } + + SampleModel sm = sampleModel.createCompatibleSampleModel(w,h); + + return new IntegerNIORaster(sm, new DataBufferNIOInt(w * h), new Point(0,0)); + } + + public WritableRaster createCompatibleWritableRaster(Rectangle rect) { + if (rect == null) { + throw new NullPointerException("Rect cannot be null"); + } + return createCompatibleWritableRaster(rect.x, rect.y, rect.width, rect.height); + } + + public WritableRaster createCompatibleWritableRaster(int x, int y, int w, int h) { + WritableRaster ret = createCompatibleWritableRaster(w, h); + return ret.createWritableChild(0,0,w,h,x,y,null); + } + + + public IntBuffer getBuffer() { + return data; + } + + public String toString() { + return new String ("IntegerNIORaster: width = "+width + +" height = " + height + +" #Bands = " + numBands + +" xOff = "+sampleModelTranslateX + +" yOff = "+sampleModelTranslateY); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/java2d/OSXOffScreenSurfaceData.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,654 @@ +/* + * Copyright (c) 2011, 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 sun.java2d; + +import java.awt.*; +import java.awt.color.*; +import java.awt.image.*; +import java.nio.*; + +import sun.awt.image.*; +import sun.java2d.loops.*; + +public class OSXOffScreenSurfaceData extends OSXSurfaceData // implements RasterListener +{ + private static native void initIDs(); + + static { + initIDs(); + } + + // the image associated with this surface + BufferedImage bim; + // the image associated with this custom surface + BufferedImage bimBackup; + // <rdar://problem/4177639> nio based images use ARGB_PRE + static DirectColorModel dcmBackup = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000, true, DataBuffer.TYPE_INT); + + Object lock; + + // cached rasters for easy access + WritableRaster bufImgRaster; + SunWritableRaster bufImgSunRaster; + + // these are extra image types we can handle + private static final int TYPE_3BYTE_RGB = BufferedImage.TYPE_BYTE_INDEXED + 1; + + // these are for callbacks when pixes have been touched + protected ByteBuffer fImageInfo; + IntBuffer fImageInfoInt; + private static final int kNeedToSyncFromJavaPixelsIndex = 0; + private static final int kNativePixelsChangedIndex = 1; + private static final int kImageStolenIndex = 2; + private static final int kSizeOfParameters = kImageStolenIndex + 1; + + public static native SurfaceData getSurfaceData(BufferedImage bufImg); + + protected static native void setSurfaceData(BufferedImage bufImg, SurfaceData sData); + + public static SurfaceData createData(BufferedImage bufImg) { + /* + * if ((bufImg.getWidth() == 32) && (bufImg.getHeight() == 32)) { Thread.dumpStack(); } + */ + // This could be called from multiple threads. We need to synchronized on the image so that + // we can ensure that only one surface data is created per image. (<rdar://4564873>) + // Note: Eventually, we should switch to using the same mechanism (CachingSurfaceManager) that Sun uses + // <rdar://4563741> + synchronized (bufImg) { + SurfaceData sData = getSurfaceData(bufImg); + if (sData != null) { return sData; } + + OSXOffScreenSurfaceData osData = OSXOffScreenSurfaceData.createNewSurface(bufImg); + + OSXOffScreenSurfaceData.setSurfaceData(bufImg, osData); + osData.cacheRasters(bufImg); +// osData.setRasterListener(); + + return osData; + } + } + + public static SurfaceData createData(Raster ras, ColorModel cm) { + throw new InternalError("SurfaceData not implemented for Raster/CM"); + } + + static OSXOffScreenSurfaceData createNewSurface(BufferedImage bufImg) { + SurfaceData sData = null; + + ColorModel cm = bufImg.getColorModel(); + int type = bufImg.getType(); + // REMIND: Check the image type and pick an appropriate subclass + switch (type) { + case BufferedImage.TYPE_INT_BGR: + sData = createDataIC(bufImg, SurfaceType.IntBgr); + break; + case BufferedImage.TYPE_INT_RGB: + sData = createDataIC(bufImg, SurfaceType.IntRgb); + break; + case BufferedImage.TYPE_INT_ARGB: + sData = createDataIC(bufImg, SurfaceType.IntArgb); + break; + case BufferedImage.TYPE_INT_ARGB_PRE: + sData = createDataIC(bufImg, SurfaceType.IntArgbPre); + break; + case BufferedImage.TYPE_3BYTE_BGR: + sData = createDataBC(bufImg, SurfaceType.ThreeByteBgr, 2); + break; + case BufferedImage.TYPE_4BYTE_ABGR: + sData = createDataBC(bufImg, SurfaceType.FourByteAbgr, 3); + break; + case BufferedImage.TYPE_4BYTE_ABGR_PRE: + sData = createDataBC(bufImg, SurfaceType.FourByteAbgrPre, 3); + break; + case BufferedImage.TYPE_USHORT_565_RGB: + sData = createDataSC(bufImg, SurfaceType.Ushort565Rgb, null); + break; + case BufferedImage.TYPE_USHORT_555_RGB: + sData = createDataSC(bufImg, SurfaceType.Ushort555Rgb, null); + break; + case BufferedImage.TYPE_BYTE_INDEXED: { + SurfaceType sType; + switch (cm.getTransparency()) { + case OPAQUE: + if (isOpaqueGray((IndexColorModel) cm)) { + sType = SurfaceType.Index8Gray; + } else { + sType = SurfaceType.ByteIndexedOpaque; + } + break; + case BITMASK: + sType = SurfaceType.ByteIndexedBm; + break; + case TRANSLUCENT: + sType = SurfaceType.ByteIndexed; + break; + default: + throw new InternalError("Unrecognized transparency"); + } + sData = createDataBC(bufImg, sType, 0); + } + break; + case BufferedImage.TYPE_BYTE_GRAY: + sData = createDataBC(bufImg, SurfaceType.ByteGray, 0); + break; + case BufferedImage.TYPE_USHORT_GRAY: + sData = createDataSC(bufImg, SurfaceType.UshortGray, null); + break; + case BufferedImage.TYPE_BYTE_BINARY: + case BufferedImage.TYPE_CUSTOM: + default: { + Raster raster = bufImg.getRaster(); + + // we try to fit a custom image into one of the predefined BufferedImages (BufferedImage does that + // first, we further refine it here) + // we can do that because a pointer in C is a pointer (pixel pointer not dependent on DataBuffer type) + SampleModel sm = bufImg.getSampleModel(); + SurfaceType sType = SurfaceType.Custom; + int transferType = cm.getTransferType(); + int pixelSize = cm.getPixelSize(); + int numOfComponents = cm.getNumColorComponents(); + if ((numOfComponents == 3) && (cm instanceof ComponentColorModel) && (sm instanceof PixelInterleavedSampleModel)) { + int sizes[] = cm.getComponentSize(); + boolean validsizes = (sizes[0] == 8) && (sizes[1] == 8) && (sizes[2] == 8); + int[] offs = ((ComponentSampleModel) sm).getBandOffsets(); + int numBands = raster.getNumBands(); + boolean bigendian = (offs[0] == numBands - 3) && (offs[1] == numBands - 2) && (offs[2] == numBands - 1); + boolean littleendian = (offs[0] == numBands - 1) && (offs[1] == numBands - 2) && (offs[2] == numBands - 3); + + if ((pixelSize == 32) && (transferType == DataBuffer.TYPE_INT)) { + if (validsizes && bigendian && cm.hasAlpha() && cm.isAlphaPremultiplied() && sizes[3] == 8) { + try { + sData = createDataIC(bufImg, sType, BufferedImage.TYPE_INT_ARGB_PRE); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && bigendian && cm.hasAlpha() && sizes[3] == 8) { + try { + sData = createDataIC(bufImg, sType, BufferedImage.TYPE_INT_ARGB); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && littleendian && cm.hasAlpha() && cm.isAlphaPremultiplied() && sizes[3] == 8) { + try { + sData = createDataIC(bufImg, sType, BufferedImage.TYPE_4BYTE_ABGR_PRE); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && littleendian && cm.hasAlpha() && sizes[3] == 8) { + try { + sData = createDataIC(bufImg, sType, BufferedImage.TYPE_4BYTE_ABGR); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && bigendian) { + try { + sData = createDataIC(bufImg, sType, BufferedImage.TYPE_INT_RGB); + } catch (ClassCastException e) { + sData = null; + } + } + } else if ((pixelSize == 32) && (transferType == DataBuffer.TYPE_BYTE)) { + if (validsizes && bigendian && cm.hasAlpha() && cm.isAlphaPremultiplied() && sizes[3] == 8) { + try { + sData = createDataBC(bufImg, sType, 3, BufferedImage.TYPE_INT_ARGB_PRE); + } catch (ClassCastException e) { + sData = null; + } + } + if (validsizes && bigendian && cm.hasAlpha() && sizes[3] == 8) { + try { + sData = createDataBC(bufImg, sType, 3, BufferedImage.TYPE_INT_ARGB); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && littleendian && cm.hasAlpha() && cm.isAlphaPremultiplied() && sizes[3] == 8) { + try { + sData = createDataBC(bufImg, sType, 3, BufferedImage.TYPE_4BYTE_ABGR_PRE); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && littleendian && cm.hasAlpha() && sizes[3] == 8) { + try { + sData = createDataBC(bufImg, sType, 3, BufferedImage.TYPE_4BYTE_ABGR); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && littleendian) { + try { + sData = createDataBC(bufImg, sType, 3, BufferedImage.TYPE_INT_BGR); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && bigendian) { + try { + sData = createDataBC(bufImg, sType, 3, BufferedImage.TYPE_INT_RGB); + } catch (ClassCastException e) { + sData = null; + } + } + } else if ((pixelSize == 24) && (transferType == DataBuffer.TYPE_INT)) { + if (validsizes && bigendian) { + try { + sData = createDataIC(bufImg, sType, BufferedImage.TYPE_INT_RGB); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && littleendian) { + try { + sData = createDataIC(bufImg, sType, BufferedImage.TYPE_INT_BGR); + } catch (ClassCastException e) { + sData = null; + } + } + } else if ((pixelSize == 24) && (transferType == DataBuffer.TYPE_BYTE)) { + if (validsizes && bigendian) { + try { + sData = createDataBC(bufImg, sType, 0, TYPE_3BYTE_RGB); + } catch (ClassCastException e) { + sData = null; + } + } else if (validsizes && littleendian) { + try { + sData = createDataBC(bufImg, sType, 0, BufferedImage.TYPE_3BYTE_BGR); + } catch (ClassCastException e) { + sData = null; + } + } + } else if ((pixelSize == 16) && (transferType == DataBuffer.TYPE_USHORT)) { + validsizes = (sizes[0] == 5) && (sizes[1] == 6) && (sizes[2] == 5); + if (validsizes && bigendian) { + try { + sData = createDataSC(bufImg, sType, null, BufferedImage.TYPE_USHORT_565_RGB); + } catch (ClassCastException e) { + sData = null; + } + } + } else if ((pixelSize == 16) && (transferType == DataBuffer.TYPE_BYTE)) { + validsizes = (sizes[0] == 5) && (sizes[1] == 6) && (sizes[2] == 5); + if (validsizes && bigendian) { + try { + sData = createDataBC(bufImg, sType, 1, BufferedImage.TYPE_USHORT_565_RGB); + } catch (ClassCastException e) { + sData = null; + } + } + } else if ((pixelSize == 15) && (transferType == DataBuffer.TYPE_USHORT)) { + validsizes = (sizes[0] == 5) && (sizes[1] == 5) && (sizes[2] == 5); + if (validsizes && bigendian) { + try { + sData = createDataSC(bufImg, sType, null, BufferedImage.TYPE_USHORT_555_RGB); + } catch (ClassCastException e) { + sData = null; + } + } + } else if ((pixelSize == 15) && (transferType == DataBuffer.TYPE_BYTE)) { + validsizes = (sizes[0] == 5) && (sizes[1] == 5) && (sizes[2] == 5); + if (validsizes && bigendian) { + try { + sData = createDataBC(bufImg, sType, 1, BufferedImage.TYPE_USHORT_555_RGB); + } catch (ClassCastException e) { + sData = null; + } + } + } + } + } + break; + } + + // we failed to match + if (sData == null) { + sData = new OSXOffScreenSurfaceData(bufImg, SurfaceType.Custom); + OSXOffScreenSurfaceData offsd = (OSXOffScreenSurfaceData) sData; + + // 2004_03_26 cmc: We used to use createCompatibleImage here. Now that createCompatibleImage returns + // an INT_ARGB_PRE instead of an NIO-based image, we need to explicitly create an NIO-based image. + IntegerNIORaster backupRaster = (IntegerNIORaster) IntegerNIORaster.createNIORaster(bufImg.getWidth(), bufImg.getHeight(), dcmBackup.getMasks(), null); + offsd.bimBackup = new BufferedImage(dcmBackup, backupRaster, dcmBackup.isAlphaPremultiplied(), null); + + // the trick that makes it work - assign the raster from backup to the surface data of the original image + offsd.initCustomRaster(backupRaster.getBuffer(), + backupRaster.getWidth(), + backupRaster.getHeight(), + offsd.fGraphicsStates, + offsd.fGraphicsStatesObject, + offsd.fImageInfo); + + //offsd.checkIfLazyPixelConversionDisabled(); + offsd.fImageInfoInt.put(kImageStolenIndex, 1); + } + + return (OSXOffScreenSurfaceData) sData; + } + + private static SurfaceData createDataIC(BufferedImage bImg, SurfaceType sType, int iType) { + OSXOffScreenSurfaceData offsd = new OSXOffScreenSurfaceData(bImg, sType); + + IntegerComponentRaster icRaster = (IntegerComponentRaster) bImg.getRaster(); + offsd.initRaster(icRaster.getDataStorage(), + icRaster.getDataOffset(0) * 4, + icRaster.getWidth(), + icRaster.getHeight(), + icRaster.getPixelStride() * 4, + icRaster.getScanlineStride() * 4, + null, + iType, + offsd.fGraphicsStates, + offsd.fGraphicsStatesObject, + offsd.fImageInfo); + + // offsd.checkIfLazyPixelConversionDisabled(); + offsd.fImageInfoInt.put(kImageStolenIndex, 1); + return offsd; + } + + public static SurfaceData createDataIC(BufferedImage bImg, SurfaceType sType) { + return createDataIC(bImg, sType, bImg.getType()); + } + + private static SurfaceData createDataSC(BufferedImage bImg, SurfaceType sType, IndexColorModel icm, int iType) { + OSXOffScreenSurfaceData offsd = new OSXOffScreenSurfaceData(bImg, sType); + + ShortComponentRaster scRaster = (ShortComponentRaster) bImg.getRaster(); + offsd.initRaster(scRaster.getDataStorage(), + scRaster.getDataOffset(0) * 2, + scRaster.getWidth(), + scRaster.getHeight(), + scRaster.getPixelStride() * 2, + scRaster.getScanlineStride() * 2, + icm, + iType, + offsd.fGraphicsStates, + offsd.fGraphicsStatesObject, + offsd.fImageInfo); + + //offsd.checkIfLazyPixelConversionDisabled(); + offsd.fImageInfoInt.put(kImageStolenIndex, 1); + return offsd; + } + + public static SurfaceData createDataSC(BufferedImage bImg, SurfaceType sType, IndexColorModel icm) { + return createDataSC(bImg, sType, icm, bImg.getType()); + } + + private static SurfaceData createDataBC(BufferedImage bImg, SurfaceType sType, int primaryBank, int iType) { + OSXOffScreenSurfaceData offsd = new OSXOffScreenSurfaceData(bImg, sType); + + ByteComponentRaster bcRaster = (ByteComponentRaster) bImg.getRaster(); + ColorModel cm = bImg.getColorModel(); + IndexColorModel icm = ((cm instanceof IndexColorModel) ? (IndexColorModel) cm : null); + offsd.initRaster(bcRaster.getDataStorage(), + bcRaster.getDataOffset(primaryBank), + bcRaster.getWidth(), + bcRaster.getHeight(), + bcRaster.getPixelStride(), + bcRaster.getScanlineStride(), + icm, + iType, + offsd.fGraphicsStates, + offsd.fGraphicsStatesObject, + offsd.fImageInfo); + + offsd.fImageInfoInt.put(kImageStolenIndex, 1); + + return offsd; + } + + public static SurfaceData createDataBC(BufferedImage bImg, SurfaceType sType, int primaryBank) { + return createDataBC(bImg, sType, primaryBank, bImg.getType()); + } + + private static SurfaceData createDataBP(BufferedImage bImg, SurfaceType sType, int iType) { + OSXOffScreenSurfaceData offsd = new OSXOffScreenSurfaceData(bImg, sType); + + BytePackedRaster bpRaster = (BytePackedRaster) bImg.getRaster(); + ColorModel cm = bImg.getColorModel(); + IndexColorModel icm = ((cm instanceof IndexColorModel) ? (IndexColorModel) cm : null); + offsd.initRaster(bpRaster.getDataStorage(), + bpRaster.getDataBitOffset(), // in bits, NOT bytes! (needs special attention in native + // code!) + bpRaster.getWidth(), + bpRaster.getHeight(), + bpRaster.getPixelBitStride(), + bpRaster.getScanlineStride() * 8, + icm, + iType, + offsd.fGraphicsStates, + offsd.fGraphicsStatesObject, + offsd.fImageInfo); + + //offsd.checkIfLazyPixelConversionDisabled(); + offsd.fImageInfoInt.put(kImageStolenIndex, 1); + return offsd; + } + + protected native void initRaster(Object theArray, int offset, int width, int height, int pixStr, int scanStr, IndexColorModel icm, int type, ByteBuffer graphicsStates, Object graphicsStatesObjects, ByteBuffer imageInfo); + + protected native void initCustomRaster(IntBuffer buffer, int width, int height, ByteBuffer graphicsStates, Object graphicsStatesObjects, ByteBuffer imageInfo); + + public Object getLockObject() { + return this.lock; + } + + // Makes the constructor package private instead of public. + OSXOffScreenSurfaceData(BufferedImage bufImg, SurfaceType sType) { + super(sType, bufImg.getColorModel()); + setBounds(0, 0, bufImg.getWidth(), bufImg.getHeight()); + + this.bim = bufImg; + + this.fImageInfo = ByteBuffer.allocateDirect(4 * kSizeOfParameters); + this.fImageInfo.order(ByteOrder.nativeOrder()); + this.fImageInfoInt = this.fImageInfo.asIntBuffer(); + + this.fImageInfoInt.put(kNeedToSyncFromJavaPixelsIndex, 1); // need to sync from Java the very first time + this.fImageInfoInt.put(kNativePixelsChangedIndex, 0); + this.fImageInfoInt.put(kImageStolenIndex, 0); + + this.lock = new Object(); + } + + /** + * Performs a copyArea within this surface. + */ + public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) { + // <rdar://problem/4488745> For the Sun2D renderer we should rely on the implementation of the super class. + // BufImageSurfaceData.java doesn't have an implementation of copyArea() and relies on the super class. + + int offsetX = 0; + int offsetY = 0; + if (sg2d.transformState == SunGraphics2D.TRANSFORM_ANY_TRANSLATE || + sg2d.transformState == SunGraphics2D.TRANSFORM_INT_TRANSLATE) { + offsetX = (int) sg2d.transform.getTranslateX(); + offsetY = (int) sg2d.transform.getTranslateY(); + } else if (sg2d.transformState != SunGraphics2D.TRANSFORM_ISIDENT) { return false; } + + // reset the clip (this is how it works on windows) + // we actually can handle a case with any clips but windows ignores the light clip + Shape clip = sg2d.getClip(); + sg2d.setClip(getBounds()); + + // clip copyArea + Rectangle clippedCopyAreaRect = clipCopyArea(sg2d, x, y, w, h, dx, dy); + if (clippedCopyAreaRect == null) { + // clipped out + return true; + } + + // the rectangle returned from clipCopyArea() is in the coordinate space of the surface (image) + // we need to substract the offsetX and offsetY to move it to the coordinate space of the graphics2d. + // sg2d.drawImage expects the destination rect to be in the coord space of the graphics2d. <rdar://3746194> + // (vm) + x = clippedCopyAreaRect.x - offsetX; + y = clippedCopyAreaRect.y - offsetY; + w = clippedCopyAreaRect.width; + h = clippedCopyAreaRect.height; + + // copy (dst coordinates are in the coord space of the graphics2d, and src coordinates are + // in the coordinate space of the image) + sg2d.drawImage(this.bim, x + dx, y + dy, x + dx + w, y + dy + h, x + offsetX, y + offsetY, x + w + offsetX, y + h + offsetY, null); + + // restore the clip + sg2d.setClip(clip); + + return true; + } + + /** + * Performs a copyarea from this surface to a buffered image. If null is passed in for the image a new image will be + * created. + * + * Only used by compositor code (private API) + */ + public BufferedImage copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, BufferedImage dstImage) { + // create the destination image if needed + if (dstImage == null) { + dstImage = getDeviceConfiguration().createCompatibleImage(w, h); + } + + // copy + Graphics g = dstImage.createGraphics(); + g.drawImage(this.bim, 0, 0, w, h, x, y, x + w, y + h, null); + g.dispose(); + + return dstImage; + } + + public boolean xorSurfacePixels(SunGraphics2D sg2d, BufferedImage srcPixels, int x, int y, int w, int h, int colorXOR) { + + int type = this.bim.getType(); + + if ((type == BufferedImage.TYPE_INT_ARGB_PRE) || (type == BufferedImage.TYPE_INT_ARGB) || (type == BufferedImage.TYPE_INT_RGB)) { return xorSurfacePixels(createData(srcPixels), colorXOR, x, y, w, h); } + + return false; + } + + native boolean xorSurfacePixels(SurfaceData src, int colorXOR, int x, int y, int w, int h); + + public void clearRect(BufferedImage bim, int w, int h) { + OSXOffScreenSurfaceData offsd = (OSXOffScreenSurfaceData) (OSXOffScreenSurfaceData.createData(bim)); + // offsd.clear(); + if (offsd.clearSurfacePixels(w, h) == false) { + Graphics2D g = bim.createGraphics(); + g.setComposite(AlphaComposite.Clear); + g.fillRect(0, 0, w, h); + g.dispose(); + } + } + + native boolean clearSurfacePixels(int w, int h); + + // 04/06/04 cmc: radr://3612381 Graphics.drawImage ignores bgcolor parameter. + // getCopyWithBgColor returns a new version of an image, drawn with a background + // color. Called by blitImage in OSXSurfaceData.java. + BufferedImage copyWithBgColor_cache = null; + + public SurfaceData getCopyWithBgColor(Color bgColor) { + int bimW = this.bim.getWidth(); + int bimH = this.bim.getHeight(); + + if ((this.copyWithBgColor_cache == null) + || (this.copyWithBgColor_cache.getWidth() < bimW) || (this.copyWithBgColor_cache.getHeight() < bimH)) { + GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); + this.copyWithBgColor_cache = gc.createCompatibleImage(bimW, bimH); + } + + Graphics g2 = this.copyWithBgColor_cache.createGraphics(); + g2.setColor(bgColor); + g2.fillRect(0, 0, bimW, bimH); + g2.drawImage(this.bim, 0, 0, bimW, bimH, null); + g2.dispose(); + + return getSurfaceData(this.copyWithBgColor_cache); + } + + /** + * Invoked before the raster's contents are to be read (via one of the modifier methods in Raster such as + * getPixel()) + */ + public void rasterRead() { + if (fImageInfoInt.get(kNativePixelsChangedIndex) == 1) { + syncToJavaPixels(); + } + } + + /** + * Invoked before the raster's contents are to be written to (via one of the modifier methods in Raster such as + * setPixel()) + */ + public void rasterWrite() { + if (fImageInfoInt.get(kNativePixelsChangedIndex) == 1) { + syncToJavaPixels(); + } + + fImageInfoInt.put(kNeedToSyncFromJavaPixelsIndex, 1); // the pixels will change + } + +// /** +// * Invoked when the raster's contents will be taken (via the Raster.getDataBuffer() method) +// */ +// public void rasterStolen() { +// fImageInfoInt.put(kImageStolenIndex, 1); // this means we must convert between Java and native pixels every +// // single primitive! (very expensive) +// if (fImageInfoInt.get(kNativePixelsChangedIndex) == 1) { +// syncToJavaPixels(); +// } +// +// // we know the pixels have been stolen, no need to listen for changes any more +//// if (this.bufImgSunRaster != null) { +//// this.bufImgSunRaster.setRasterListener(null); +//// } +// } + + private native void syncToJavaPixels(); + + // we need to refer to rasters often, so cache them + void cacheRasters(BufferedImage bim) { + this.bufImgRaster = bim.getRaster(); + if (this.bufImgRaster instanceof SunWritableRaster) { + this.bufImgSunRaster = (SunWritableRaster) this.bufImgRaster; + } + } + +// void setRasterListener() { +// if (this.bufImgSunRaster != null) { +// this.bufImgSunRaster.setRasterListener(this); +// +// Raster parentRaster = this.bufImgSunRaster.getParent(); +// if (parentRaster != null) { +// if (parentRaster instanceof SunWritableRaster) { +// // mark subimages stolen to turn off lazy pixel conversion (gznote: can we do better here?) +// ((SunWritableRaster) parentRaster).notifyStolen(); +// } +// rasterStolen(); +// } +// } else { +// // it's a custom image (non-natively supported) and we can not set a raster listener +// // so mark the image as stolen - this will turn off LazyPixelConversion optimization (slow, but correct) +// rasterStolen(); +// } +// } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/java2d/OSXSurfaceData.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,1166 @@ +/* + * Copyright (c) 2011, 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 sun.java2d; + +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.nio.*; + +import sun.awt.*; +import sun.awt.image.*; +import sun.java2d.loops.*; +import sun.java2d.pipe.*; +import sun.lwawt.macosx.*; + +/* + * This is the SurfaceData for a CGContextRef. + */ +public abstract class OSXSurfaceData extends BufImgSurfaceData { + final static float UPPER_BND = Float.MAX_VALUE / 2.0f; + final static float LOWER_BND = -UPPER_BND; + + protected static CRenderer sQuartzPipe = null; + protected static CTextPipe sCocoaTextPipe = null; + protected static CompositeCRenderer sQuartzCompositePipe = null; + + private GraphicsConfiguration fConfig; + private Rectangle fBounds; // bounds in user coordinates + + static { + sQuartzPipe = new CRenderer(); // Creates the singleton quartz pipe. + } + + // NOTE: Any subclasses must eventually call QuartzSurfaceData_InitOps in OSXSurfaceData.h + // This sets up the native side for the SurfaceData, and is required. + public OSXSurfaceData(SurfaceType sType, ColorModel cm) { + this(sType, cm, null, new Rectangle()); + } + + public OSXSurfaceData(SurfaceType sType, ColorModel cm, GraphicsConfiguration config, Rectangle bounds) { + super(sType, cm); + + this.fConfig = config; + + this.fBounds = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.y + bounds.height); + + this.fGraphicsStates = getBufferOfSize(kSizeOfParameters); + this.fGraphicsStatesInt = this.fGraphicsStates.asIntBuffer(); + this.fGraphicsStatesFloat = this.fGraphicsStates.asFloatBuffer(); + this.fGraphicsStatesLong = this.fGraphicsStates.asLongBuffer(); + this.fGraphicsStatesObject = new Object[6]; // clip coordinates + clip types + texture paint image + stroke dash + // array + font + font paint + + // NOTE: All access to the DrawingQueue comes through this OSXSurfaceData instance. Therefore + // every instance method of OSXSurfaceData that accesses the fDrawingQueue is synchronized. + + // Thread.dumpStack(); + } + + public void validatePipe(SunGraphics2D sg2d) { + + if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) { + if (sCocoaTextPipe == null) { + sCocoaTextPipe = new CTextPipe(); + } + + sg2d.imagepipe = sQuartzPipe; + sg2d.drawpipe = sQuartzPipe; + sg2d.fillpipe = sQuartzPipe; + sg2d.shapepipe = sQuartzPipe; + sg2d.textpipe = sCocoaTextPipe; + } else { + setPipesToQuartzComposite(sg2d); + } + } + + protected void setPipesToQuartzComposite(SunGraphics2D sg2d) { + if (sQuartzCompositePipe == null) { + sQuartzCompositePipe = new CompositeCRenderer(); + } + + if (sCocoaTextPipe == null) { + sCocoaTextPipe = new CTextPipe(); + } + + sg2d.imagepipe = sQuartzCompositePipe; + sg2d.drawpipe = sQuartzCompositePipe; + sg2d.fillpipe = sQuartzCompositePipe; + sg2d.shapepipe = sQuartzCompositePipe; + sg2d.textpipe = sCocoaTextPipe; + } + + public Rectangle getBounds() { + // gznote: always return a copy, not the rect itself and translate into device space + return new Rectangle(fBounds.x, fBounds.y, fBounds.width, fBounds.height - fBounds.y); + } + + public GraphicsConfiguration getDeviceConfiguration() { + return fConfig; + } + + protected void setBounds(int x, int y, int w, int h) { + fBounds.reshape(x, y, w, y + h); + } + + // START compositing support API + public abstract BufferedImage copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, BufferedImage image); + + public abstract boolean xorSurfacePixels(SunGraphics2D sg2d, BufferedImage srcPixels, int x, int y, int w, int h, int colorXOR); + + GraphicsConfiguration sDefaultGraphicsConfiguration = null; + + protected BufferedImage getCompositingImage(int w, int h) { + if (sDefaultGraphicsConfiguration == null) { + sDefaultGraphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); + } + + BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE); + // clear the image. + clearRect(img, w, h); + return img; + } + + protected BufferedImage getCompositingImageSame(BufferedImage img, int w, int h) { + if ((img == null) || (img.getWidth() != w) || (img.getHeight() != h)) { + img = getCompositingImage(w, h); + } + return img; + } + + BufferedImage sSrcComposite = null; + + public BufferedImage getCompositingSrcImage(int w, int h) { + // <rdar://problem/3720263>. Changed from getCompositingImageBiggerOrSame() to + // getCompositingImageSame(). (vm) + BufferedImage bim = getCompositingImageSame(sSrcComposite, w, h); + sSrcComposite = bim; + return bim; + } + + BufferedImage sDstInComposite = null; + + public BufferedImage getCompositingDstInImage(int w, int h) { + BufferedImage bim = getCompositingImageSame(sDstInComposite, w, h); + sDstInComposite = bim; + return bim; + } + + BufferedImage sDstOutComposite = null; + + public BufferedImage getCompositingDstOutImage(int w, int h) { + BufferedImage bim = getCompositingImageSame(sDstOutComposite, w, h); + sDstOutComposite = bim; + return bim; + } + + public void clearRect(BufferedImage bim, int w, int h) { + Graphics2D g = bim.createGraphics(); + g.setComposite(AlphaComposite.Clear); + g.fillRect(0, 0, w, h); + g.dispose(); + } + + // END compositing support API + + public void invalidate() { + // always valid + } + + // graphics primitives drawing implementation: + + // certain primitives don't care about all the states (ex. drawing an image needs not involve setting current paint) + static final int kPrimitive = 0; + static final int kImage = 1; + static final int kText = 2; + static final int kCopyArea = 3; + static final int kExternal = 4; + + static final int kLine = 5; // belongs to kPrimitive + static final int kRect = 6; // belongs to kPrimitive + static final int kRoundRect = 7; // belongs to kPrimitive + static final int kOval = 8; // belongs to kPrimitive + static final int kArc = 9; // belongs to kPrimitive + static final int kPolygon = 10; // belongs to kPrimitive + static final int kShape = 11; // belongs to kPrimitive + // static final int kImage = 12; // belongs to kImage + static final int kString = 13; // belongs to kText + static final int kGlyphs = 14; // belongs to kText + static final int kUnicodes = 15; // belongs to kText + // static final int kCopyArea = 16; // belongs to kCopyArea + // static final int kExternal = 17; // belongs to kExternal + + static final int kCommonParameterCount = 1 + 1 + 4 + 4; // type + change flags + color info (type(1) align(1) and + // value(2)) + parameters ((x1, y1, x2, y2) OR (x, y, w, h)) + static final int kLineParametersCount = kCommonParameterCount; // kCommonParameterCount + static final int kRectParametersCount = kCommonParameterCount + 1; // kCommonParameterCount + isfill + static final int kRoundRectParametersCount = kCommonParameterCount + 2 + 1; // kCommonParameterCount + arcW + arcH + + // isfill + static final int kOvalParametersCount = kCommonParameterCount + 1; // kCommonParameterCount + isfill + static final int kArcParametersCount = kCommonParameterCount + 2 + 1 + 1;// kCommonParameterCount + startAngle + + // arcAngle + isfill + type + static final int kPolygonParametersCount = 0; // not supported + static final int kShapeParametersCount = 0; // not supported + static final int kImageParametersCount = kCommonParameterCount + 2 + 2 + 4 + 4; // flip horz vert + w&h + src + dst + static final int kStringParametersCount = 0; // not supported + static final int kGlyphsParametersCount = 0; // not supported + static final int kUnicodesParametersCount = 0; // not supported + static final int kPixelParametersCount = 0; // not supported + static final int kExternalParametersCount = 0; // not supported + + // for intParameters + // states info + static final int kChangeFlagIndex = 0; // kBoundsChangedBit | .. | kFontChangedBit + // bounds info + static final int kBoundsXIndex = 1; + static final int kBoundsYIndex = 2; + static final int kBoundsWidthIndex = 3; + static final int kBoundsHeightIndex = 4; + // clip info + static final int kClipStateIndex = 5; + static final int kClipNumTypesIndex = 6; + static final int kClipNumCoordsIndex = 7; + static final int kClipWindingRuleIndex = 8; + static final int kClipXIndex = 9; + static final int kClipYIndex = 10; + static final int kClipWidthIndex = 11; + static final int kClipHeightIndex = 12; + // ctm info + static final int kCTMaIndex = 13; + static final int kCTMbIndex = 14; + static final int kCTMcIndex = 15; + static final int kCTMdIndex = 16; + static final int kCTMtxIndex = 17; + static final int kCTMtyIndex = 18; + // color info + static final int kColorStateIndex = 19; // kColorSimple or kColorGradient or kColorTexture + static final int kColorRGBValueIndex = 20; // if kColorSimple + static final int kColorIndexValueIndex = 21; // if kColorSystem + static final int kColorPointerIndex = 22; // + static final int kColorPointerIndex2 = 23; // + static final int kColorRGBValue1Index = 24; // if kColorGradient + static final int kColorWidthIndex = 25; // if kColorTexture + static final int kColorRGBValue2Index = 26; // if kColorGradient + static final int kColorHeightIndex = 27; // if kColorTexture + static final int kColorIsCyclicIndex = 28; // if kColorGradient (kColorNonCyclic or kColorCyclic) + static final int kColorx1Index = 29; + static final int kColortxIndex = 30; + static final int kColory1Index = 31; + static final int kColortyIndex = 32; + static final int kColorx2Index = 33; + static final int kColorsxIndex = 34; + static final int kColory2Index = 35; + static final int kColorsyIndex = 36; + // composite info + static final int kCompositeRuleIndex = 37; // kCGCompositeClear or ... or kCGCompositeXor + static final int kCompositeValueIndex = 38; + // stroke info + static final int kStrokeJoinIndex = 39; // see BasicStroke.java + static final int kStrokeCapIndex = 40; // see BasicStroke.java + static final int kStrokeWidthIndex = 41; + static final int kStrokeDashPhaseIndex = 42; + static final int kStrokeLimitIndex = 43; + // hints info + static final int kHintsAntialiasIndex = 44; + static final int kHintsTextAntialiasIndex = 45; + static final int kHintsFractionalMetricsIndex = 46; + static final int kHintsRenderingIndex = 47; + static final int kHintsInterpolationIndex = 48; + // live resizing info + static final int kCanDrawDuringLiveResizeIndex = 49; + + static final int kSizeOfParameters = kCanDrawDuringLiveResizeIndex + 1; + + // for objectParameters + static final int kClipCoordinatesIndex = 0; + static final int kClipTypesIndex = 1; + static final int kTextureImageIndex = 2; + static final int kStrokeDashArrayIndex = 3; + static final int kFontIndex = 4; + static final int kFontPaintIndex = 5; + + // possible state changes + static final int kBoundsChangedBit = 1 << 0; + static final int kBoundsNotChangedBit = ~kBoundsChangedBit; + static final int kClipChangedBit = 1 << 1; + static final int kClipNotChangedBit = ~kClipChangedBit; + static final int kCTMChangedBit = 1 << 2; + static final int kCTMNotChangedBit = ~kCTMChangedBit; + static final int kColorChangedBit = 1 << 3; + static final int kColorNotChangedBit = ~kColorChangedBit; + static final int kCompositeChangedBit = 1 << 4; + static final int kCompositeNotChangedBit = ~kCompositeChangedBit; + static final int kStrokeChangedBit = 1 << 5; + static final int kStrokeNotChangedBit = ~kStrokeChangedBit; + static final int kHintsChangedBit = 1 << 6; + static final int kHintsNotChangedBit = ~kHintsChangedBit; + static final int kFontChangedBit = 1 << 7; + static final int kFontNotChangedBit = ~kFontChangedBit; + static final int kEverythingChangedFlag = 0xffffffff; + + // possible color states + static final int kColorSimple = 0; + static final int kColorSystem = 1; + static final int kColorGradient = 2; + static final int kColorTexture = 3; + + // possible gradient color states + static final int kColorNonCyclic = 0; + static final int kColorCyclic = 1; + + // possible clip states + static final int kClipRect = 0; + static final int kClipShape = 1; + + static int getRendererTypeForPrimitive(int primitiveType) { + switch (primitiveType) { + case kImage: + return kImage; + case kCopyArea: + return kCopyArea; + case kExternal: + return kExternal; + case kString: + case kGlyphs: + case kUnicodes: + return kText; + default: + return kPrimitive; + } + } + + int fChangeFlag; + protected ByteBuffer fGraphicsStates = null; + IntBuffer fGraphicsStatesInt = null; + FloatBuffer fGraphicsStatesFloat = null; + LongBuffer fGraphicsStatesLong = null; + protected Object[] fGraphicsStatesObject = null; + + Rectangle userBounds = new Rectangle(); + float lastUserX = 0; + float lastUserY = 0; + float lastUserW = 0; + float lastUserH = 0; + + void setUserBounds(SunGraphics2D sg2d, int x, int y, int width, int height) { + if ((lastUserX != x) || (lastUserY != y) || (lastUserW != width) || (lastUserH != height)) { + lastUserX = x; + lastUserY = y; + lastUserW = width; + lastUserH = height; + + this.fGraphicsStatesInt.put(kBoundsXIndex, x); + this.fGraphicsStatesInt.put(kBoundsYIndex, y); + this.fGraphicsStatesInt.put(kBoundsWidthIndex, width); + this.fGraphicsStatesInt.put(kBoundsHeightIndex, height); + + userBounds.setBounds(x, y, width, height); + + this.fChangeFlag = (this.fChangeFlag | kBoundsChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kBoundsNotChangedBit); + } + } + + static ByteBuffer getBufferOfSize(int size) { + ByteBuffer buffer = ByteBuffer.allocateDirect(size * 4); + buffer.order(ByteOrder.nativeOrder()); + return buffer; + } + + FloatBuffer clipCoordinatesArray = null; + IntBuffer clipTypesArray = null; + Shape lastClipShape = null; + float lastClipX = 0; + float lastClipY = 0; + float lastClipW = 0; + float lastClipH = 0; + + void setupClip(SunGraphics2D sg2d) { + switch (sg2d.clipState) { + case SunGraphics2D.CLIP_DEVICE: + case SunGraphics2D.CLIP_RECTANGULAR: { + Region clip = sg2d.getCompClip(); + float x = clip.getLoX(); + float y = clip.getLoY(); + float w = clip.getWidth(); + float h = clip.getHeight(); + if ((this.fGraphicsStatesInt.get(kClipStateIndex) != kClipRect) || + (x != lastClipX) || + (y != lastClipY) || + (w != lastClipW) || + (h != lastClipH)) { + this.fGraphicsStatesFloat.put(kClipXIndex, x); + this.fGraphicsStatesFloat.put(kClipYIndex, y); + this.fGraphicsStatesFloat.put(kClipWidthIndex, w); + this.fGraphicsStatesFloat.put(kClipHeightIndex, h); + + lastClipX = x; + lastClipY = y; + lastClipW = w; + lastClipH = h; + + this.fChangeFlag = (this.fChangeFlag | kClipChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kClipNotChangedBit); + } + this.fGraphicsStatesInt.put(kClipStateIndex, kClipRect); + break; + } + case SunGraphics2D.CLIP_SHAPE: { + // if (lastClipShape != sg2d.usrClip) shapes are mutable!, and doing "equals" traverses all + // the coordinates, so we might as well do all of it anyhow + lastClipShape = sg2d.usrClip; + + GeneralPath gp = null; + + if (sg2d.usrClip instanceof GeneralPath) { + gp = (GeneralPath) sg2d.usrClip; + } else { + gp = new GeneralPath(sg2d.usrClip); + } + + int shapeLength = getPathLength(gp); + + if ((clipCoordinatesArray == null) || (clipCoordinatesArray.capacity() < (shapeLength * 6))) { + clipCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a + // max of 6 coordinates + } + if ((clipTypesArray == null) || (clipTypesArray.capacity() < shapeLength)) { + clipTypesArray = getBufferOfSize(shapeLength).asIntBuffer(); + } + + int windingRule = getPathCoordinates(gp, clipCoordinatesArray, clipTypesArray); + + this.fGraphicsStatesInt.put(kClipNumTypesIndex, clipTypesArray.position()); + this.fGraphicsStatesInt.put(kClipNumCoordsIndex, clipCoordinatesArray.position()); + this.fGraphicsStatesInt.put(kClipWindingRuleIndex, windingRule); + this.fGraphicsStatesObject[kClipTypesIndex] = clipTypesArray; + this.fGraphicsStatesObject[kClipCoordinatesIndex] = clipCoordinatesArray; + + this.fChangeFlag = (this.fChangeFlag | kClipChangedBit); + this.fGraphicsStatesInt.put(kClipStateIndex, kClipShape); + break; + } + } + + } + + final double[] lastCTM = new double[6]; + float lastCTMa = 0; + float lastCTMb = 0; + float lastCTMc = 0; + float lastCTMd = 0; + float lastCTMtx = 0; + float lastCTMty = 0; + + void setupTransform(SunGraphics2D sg2d) { + sg2d.transform.getMatrix(lastCTM); + + float a = (float) lastCTM[0]; + float b = (float) lastCTM[1]; + float c = (float) lastCTM[2]; + float d = (float) lastCTM[3]; + float tx = (float) lastCTM[4]; + float ty = (float) lastCTM[5]; + if (tx != lastCTMtx || + ty != lastCTMty || + a != lastCTMa || + b != lastCTMb || + c != lastCTMc || + d != lastCTMd) { + this.fGraphicsStatesFloat.put(kCTMaIndex, a); + this.fGraphicsStatesFloat.put(kCTMbIndex, b); + this.fGraphicsStatesFloat.put(kCTMcIndex, c); + this.fGraphicsStatesFloat.put(kCTMdIndex, d); + this.fGraphicsStatesFloat.put(kCTMtxIndex, tx); + this.fGraphicsStatesFloat.put(kCTMtyIndex, ty); + + lastCTMa = a; + lastCTMb = b; + lastCTMc = c; + lastCTMd = d; + lastCTMtx = tx; + lastCTMty = ty; + + this.fChangeFlag = (this.fChangeFlag | kCTMChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kCTMNotChangedBit); + } + } + + static AffineTransform sIdentityMatrix = new AffineTransform(); + Paint lastPaint = null; + long lastPaintPtr = 0; + int lastPaintRGB = 0; + int lastPaintIndex = 0; + BufferedImage texturePaintImage = null; + + void setupPaint(SunGraphics2D sg2d, int x, int y, int w, int h) { + if (sg2d.paint instanceof SystemColor) { + SystemColor color = (SystemColor) sg2d.paint; + int index = color.hashCode(); // depends on Color.java hashCode implementation! (returns "value" of color) + if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSystem) || (index != this.lastPaintIndex)) { + this.lastPaintIndex = index; + + this.fGraphicsStatesInt.put(kColorStateIndex, kColorSystem); + this.fGraphicsStatesInt.put(kColorIndexValueIndex, index); + + this.fChangeFlag = (this.fChangeFlag | kColorChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit); + } + } else if (sg2d.paint instanceof Color) { + Color color = (Color) sg2d.paint; + int rgb = color.getRGB(); + if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSimple) || (rgb != this.lastPaintRGB)) { + this.lastPaintRGB = rgb; + + this.fGraphicsStatesInt.put(kColorStateIndex, kColorSimple); + this.fGraphicsStatesInt.put(kColorRGBValueIndex, rgb); + + this.fChangeFlag = (this.fChangeFlag | kColorChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit); + } + } else if (sg2d.paint instanceof GradientPaint) { + if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorGradient) || (lastPaint != sg2d.paint)) { + GradientPaint color = (GradientPaint) sg2d.paint; + this.fGraphicsStatesInt.put(kColorStateIndex, kColorGradient); + this.fGraphicsStatesInt.put(kColorRGBValue1Index, color.getColor1().getRGB()); + this.fGraphicsStatesInt.put(kColorRGBValue2Index, color.getColor2().getRGB()); + this.fGraphicsStatesInt.put(kColorIsCyclicIndex, (color.isCyclic()) ? kColorCyclic : kColorNonCyclic); + Point2D p = color.getPoint1(); + this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX()); + this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY()); + p = color.getPoint2(); + this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX()); + this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY()); + + this.fChangeFlag = (this.fChangeFlag | kColorChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit); + } + } else if (sg2d.paint instanceof TexturePaint) { + if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint)) { + TexturePaint color = (TexturePaint) sg2d.paint; + this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture); + texturePaintImage = color.getImage(); + SurfaceData textureSurfaceData = BufImgSurfaceData.createData(texturePaintImage); + this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth()); + this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight()); + Rectangle2D anchor = color.getAnchorRect(); + this.fGraphicsStatesFloat.put(kColortxIndex, (float) anchor.getX()); + this.fGraphicsStatesFloat.put(kColortyIndex, (float) anchor.getY()); + this.fGraphicsStatesFloat.put(kColorsxIndex, (float) (anchor.getWidth() / texturePaintImage.getWidth())); + this.fGraphicsStatesFloat.put(kColorsyIndex, (float) (anchor.getHeight() / texturePaintImage.getHeight())); + this.fGraphicsStatesObject[kTextureImageIndex] = textureSurfaceData; + + this.fChangeFlag = (this.fChangeFlag | kColorChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit); + } + } else { + if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint) || ((this.fChangeFlag & kBoundsChangedBit) != 0)) { + PaintContext context = sg2d.paint.createContext(sg2d.getDeviceColorModel(), userBounds, userBounds, sIdentityMatrix, sg2d.getRenderingHints()); + WritableRaster raster = (WritableRaster) (context.getRaster(userBounds.x, userBounds.y, userBounds.width, userBounds.height)); + ColorModel cm = context.getColorModel(); + texturePaintImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null); + + this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture); + this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth()); + this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight()); + this.fGraphicsStatesFloat.put(kColortxIndex, (float) userBounds.getX()); + this.fGraphicsStatesFloat.put(kColortyIndex, (float) userBounds.getY()); + this.fGraphicsStatesFloat.put(kColorsxIndex, 1.0f); + this.fGraphicsStatesFloat.put(kColorsyIndex, 1.0f); + this.fGraphicsStatesObject[kTextureImageIndex] = sun.awt.image.BufImgSurfaceData.createData(texturePaintImage); + + context.dispose(); + + this.fChangeFlag = (this.fChangeFlag | kColorChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit); + } + } + lastPaint = sg2d.paint; + } + + Composite lastComposite; + int lastCompositeAlphaRule = 0; + float lastCompositeAlphaValue = 0; + + void setupComposite(SunGraphics2D sg2d) { + Composite composite = sg2d.composite; + + if (lastComposite != composite) { + lastComposite = composite; + + // For composite state COMP_ISCOPY, COMP_XOR or COMP_CUSTOM set alpha compositor to COPY: + int alphaRule = AlphaComposite.SRC_OVER; + float alphaValue = 1.0f; + + // For composite state COMP_ISCOPY composite could be null. If it's not (or composite state == COMP_ALPHA) + // get alpha compositor's values: + if ((sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) && (composite != null)) { + AlphaComposite alphaComposite = (AlphaComposite) composite; + alphaRule = alphaComposite.getRule(); + alphaValue = alphaComposite.getAlpha(); + } + + // 2-17-03 VL: [Radar 3174922] + // For COMP_XOR and COMP_CUSTOM compositing modes we should be setting alphaRule = AlphaComposite.SRC + // which should map to kCGCompositeCopy. + + if ((lastCompositeAlphaRule != alphaRule) || (lastCompositeAlphaValue != alphaValue)) { + this.fGraphicsStatesInt.put(kCompositeRuleIndex, alphaRule); + this.fGraphicsStatesFloat.put(kCompositeValueIndex, alphaValue); + + lastCompositeAlphaRule = alphaRule; + lastCompositeAlphaValue = alphaValue; + + this.fChangeFlag = (this.fChangeFlag | kCompositeChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit); + } + } else { + this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit); + } + } + + BasicStroke lastStroke = null; + static BasicStroke defaultBasicStroke = new BasicStroke(); + + void setupStroke(SunGraphics2D sg2d) { + BasicStroke stroke = defaultBasicStroke; + + if (sg2d.stroke instanceof BasicStroke) { + stroke = (BasicStroke) sg2d.stroke; + } + + if (lastStroke != stroke) { + this.fGraphicsStatesObject[kStrokeDashArrayIndex] = stroke.getDashArray(); + this.fGraphicsStatesFloat.put(kStrokeDashPhaseIndex, stroke.getDashPhase()); + this.fGraphicsStatesInt.put(kStrokeCapIndex, stroke.getEndCap()); + this.fGraphicsStatesInt.put(kStrokeJoinIndex, stroke.getLineJoin()); + this.fGraphicsStatesFloat.put(kStrokeWidthIndex, stroke.getLineWidth()); + this.fGraphicsStatesFloat.put(kStrokeLimitIndex, stroke.getMiterLimit()); + + this.fChangeFlag = (this.fChangeFlag | kStrokeChangedBit); + + lastStroke = stroke; + } else { + this.fChangeFlag = (this.fChangeFlag & kStrokeNotChangedBit); + } + } + + Font lastFont; + + void setupFont(Font font, Paint paint) { + if (font == null) { return; } + + // We have to setup the kFontPaintIndex if we have changed the color so we added the last + // test to see if the color has changed - needed for complex strings + // see Radar 3368674 + if ((font != lastFont) || ((this.fChangeFlag & kColorChangedBit) != 0)) { + this.fGraphicsStatesObject[kFontIndex] = font; + this.fGraphicsStatesObject[kFontPaintIndex] = paint; + + this.fChangeFlag = (this.fChangeFlag | kFontChangedBit); + + lastFont = font; + } else { + this.fChangeFlag = (this.fChangeFlag & kFontNotChangedBit); + } + } + + void setupRenderingHints(SunGraphics2D sg2d) { + boolean hintsChanged = false; + + // Significant for draw, fill, text, and image ops: + int antialiasHint = sg2d.antialiasHint; + if (this.fGraphicsStatesInt.get(kHintsAntialiasIndex) != antialiasHint) { + this.fGraphicsStatesInt.put(kHintsAntialiasIndex, antialiasHint); + hintsChanged = true; + } + + // Significant only for text ops: + int textAntialiasHint = sg2d.textAntialiasHint; + if (this.fGraphicsStatesInt.get(kHintsTextAntialiasIndex) != textAntialiasHint) { + this.fGraphicsStatesInt.put(kHintsTextAntialiasIndex, textAntialiasHint); + hintsChanged = true; + } + + // Significant only for text ops: + int fractionalMetricsHint = sg2d.fractionalMetricsHint; + if (this.fGraphicsStatesInt.get(kHintsFractionalMetricsIndex) != fractionalMetricsHint) { + this.fGraphicsStatesInt.put(kHintsFractionalMetricsIndex, fractionalMetricsHint); + hintsChanged = true; + } + + // Significant only for image ops: + int renderHint = sg2d.renderHint; + if (this.fGraphicsStatesInt.get(kHintsRenderingIndex) != renderHint) { + this.fGraphicsStatesInt.put(kHintsRenderingIndex, renderHint); + hintsChanged = true; + } + + // Significant only for image ops: + Object hintValue = sg2d.getRenderingHint(RenderingHints.KEY_INTERPOLATION); + int interpolationHint = (hintValue != null ? ((SunHints.Value) hintValue).getIndex() : -1); + if (this.fGraphicsStatesInt.get(kHintsInterpolationIndex) != interpolationHint) { + this.fGraphicsStatesInt.put(kHintsInterpolationIndex, interpolationHint); + hintsChanged = true; + } + + if (hintsChanged) { + this.fChangeFlag = (this.fChangeFlag | kHintsChangedBit); + } else { + this.fChangeFlag = (this.fChangeFlag & kHintsNotChangedBit); + } + } + + SunGraphics2D sg2dCurrent = null; + Thread threadCurrent = null; + + void setupGraphicsState(SunGraphics2D sg2d, int primitiveType) { + setupGraphicsState(sg2d, primitiveType, sg2d.font, 0, 0, fBounds.width, fBounds.height); // deviceBounds into userBounds + } + + void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, int x, int y, int w, int h) { + setupGraphicsState(sg2d, primitiveType, sg2d.font, x, y, w, h); + } + + // the method below is overriden by CPeerSurface to check the last peer used to draw + // if the peer changed we finish lazy drawing + void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, Font font, int x, int y, int w, int h) { + this.fChangeFlag = 0; + + setUserBounds(sg2d, x, y, w, h); + + Thread thread = Thread.currentThread(); + if ((this.sg2dCurrent != sg2d) || (this.threadCurrent != thread)) { + this.sg2dCurrent = sg2d; + this.threadCurrent = thread; + + setupClip(sg2d); + setupTransform(sg2d); + setupPaint(sg2d, x, y, w, h); + setupComposite(sg2d); + setupStroke(sg2d); + setupFont(font, sg2d.paint); + setupRenderingHints(sg2d); + + this.fChangeFlag = kEverythingChangedFlag; + } else { + int rendererType = getRendererTypeForPrimitive(primitiveType); + + setupClip(sg2d); + setupTransform(sg2d); + + if (rendererType != kCopyArea) { + setupComposite(sg2d); + setupRenderingHints(sg2d); + + if ((rendererType != kImage)) { + setupPaint(sg2d, x, y, w, h); + setupStroke(sg2d); + } + if (rendererType != kPrimitive) { + setupFont(font, sg2d.paint); + } + + } + } + + this.fGraphicsStatesInt.put(kChangeFlagIndex, this.fChangeFlag); + } + + boolean isCustomPaint(SunGraphics2D sg2d) { + if ((sg2d.paint instanceof Color) || (sg2d.paint instanceof SystemColor) || (sg2d.paint instanceof GradientPaint) || (sg2d.paint instanceof TexturePaint)) { return false; } + + return true; + } + + final float[] segmentCoordinatesArray = new float[6]; + + int getPathLength(GeneralPath gp) { + int length = 0; + + PathIterator pi = gp.getPathIterator(null); + while (pi.isDone() == false) { + pi.next(); + length++; + } + + return length; + } + + int getPathCoordinates(GeneralPath gp, FloatBuffer coordinates, IntBuffer types) { + // System.err.println("getPathCoordinates"); + boolean skip = false; + + coordinates.clear(); + types.clear(); + + int type; + + PathIterator pi = gp.getPathIterator(null); + while (pi.isDone() == false) { + skip = false; + type = pi.currentSegment(segmentCoordinatesArray); + + switch (type) { + case PathIterator.SEG_MOVETO: + // System.err.println(" SEG_MOVETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")"); + if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND && + segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) { + coordinates.put(segmentCoordinatesArray[0]); + coordinates.put(segmentCoordinatesArray[1]); + } else { + skip = true; + } + break; + case PathIterator.SEG_LINETO: + // System.err.println(" SEG_LINETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")"); + if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND && + segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) { + coordinates.put(segmentCoordinatesArray[0]); + coordinates.put(segmentCoordinatesArray[1]); + } else { + skip = true; + } + break; + case PathIterator.SEG_QUADTO: + // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+")"); + if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND && + segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND && + segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND && + segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND) { + coordinates.put(segmentCoordinatesArray[0]); + coordinates.put(segmentCoordinatesArray[1]); + coordinates.put(segmentCoordinatesArray[2]); + coordinates.put(segmentCoordinatesArray[3]); + } else { + skip = true; + } + break; + case PathIterator.SEG_CUBICTO: + // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+"), ("+segmentCoordinatesArray[4]+", "+segmentCoordinatesArray[5]+")"); + if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND && + segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND && + segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND && + segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND && + segmentCoordinatesArray[4] < UPPER_BND && segmentCoordinatesArray[4] > LOWER_BND && + segmentCoordinatesArray[5] < UPPER_BND && segmentCoordinatesArray[5] > LOWER_BND) { + coordinates.put(segmentCoordinatesArray[0]); + coordinates.put(segmentCoordinatesArray[1]); + coordinates.put(segmentCoordinatesArray[2]); + coordinates.put(segmentCoordinatesArray[3]); + coordinates.put(segmentCoordinatesArray[4]); + coordinates.put(segmentCoordinatesArray[5]); + } else { + skip = true; + } + break; + case PathIterator.SEG_CLOSE: + // System.err.println(" SEG_CLOSE"); + break; + } + + if (!skip) { + types.put(type); + } + + pi.next(); + } + + return pi.getWindingRule(); + } + + public void doLine(CRenderer renderer, SunGraphics2D sg2d, float x1, float y1, float x2, float y2) { + // System.err.println("-- doLine x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2+" paint="+sg2d.paint); + setupGraphicsState(sg2d, kLine, sg2d.font, 0, 0, fBounds.width, fBounds.height); + renderer.doLine(this, x1, y1, x2, y2); + } + + public void doRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) { + // System.err.println("-- doRect x="+x+" y="+y+" w="+width+" h="+height+" isfill="+isfill+" paint="+sg2d.paint); + if ((isfill) && (isCustomPaint(sg2d))) { + setupGraphicsState(sg2d, kRect, (int) x, (int) y, (int) width, (int) height); + } else { + setupGraphicsState(sg2d, kRect, sg2d.font, 0, 0, fBounds.width, fBounds.height); + } + renderer.doRect(this, x, y, width, height, isfill); + } + + public void doRoundRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float arcW, float arcH, boolean isfill) { + // System.err.println("--- doRoundRect"); + if ((isfill) && (isCustomPaint(sg2d))) { + setupGraphicsState(sg2d, kRoundRect, (int) x, (int) y, (int) width, (int) height); + } else { + setupGraphicsState(sg2d, kRoundRect, sg2d.font, 0, 0, fBounds.width, fBounds.height); + } + renderer.doRoundRect(this, x, y, width, height, arcW, arcH, isfill); + } + + public void doOval(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) { + // System.err.println("--- doOval"); + if ((isfill) && (isCustomPaint(sg2d))) { + setupGraphicsState(sg2d, kOval, (int) x, (int) y, (int) width, (int) height); + } else { + setupGraphicsState(sg2d, kOval, sg2d.font, 0, 0, fBounds.width, fBounds.height); + } + renderer.doOval(this, x, y, width, height, isfill); + } + + public void doArc(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float startAngle, float arcAngle, int type, boolean isfill) { + // System.err.println("--- doArc"); + if ((isfill) && (isCustomPaint(sg2d))) { + setupGraphicsState(sg2d, kArc, (int) x, (int) y, (int) width, (int) height); + } else { + setupGraphicsState(sg2d, kArc, sg2d.font, 0, 0, fBounds.width, fBounds.height); + } + + renderer.doArc(this, x, y, width, height, startAngle, arcAngle, type, isfill); + } + + public void doPolygon(CRenderer renderer, SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints, boolean ispolygon, boolean isfill) { + // System.err.println("--- doPolygon"); + + if ((isfill) && (isCustomPaint(sg2d))) { + int minx = xpoints[0]; + int miny = ypoints[0]; + int maxx = minx; + int maxy = miny; + for (int i = 1; i < npoints; i++) { + int x = xpoints[i]; + if (x < minx) { + minx = x; + } else if (x > maxx) { + maxx = x; + } + + int y = ypoints[i]; + if (y < miny) { + miny = y; + } else if (y > maxy) { + maxy = y; + } + } + setupGraphicsState(sg2d, kPolygon, minx, miny, maxx - minx, maxy - miny); + } else { + setupGraphicsState(sg2d, kPolygon, sg2d.font, 0, 0, fBounds.width, fBounds.height); + } + renderer.doPoly(this, xpoints, ypoints, npoints, ispolygon, isfill); + } + + FloatBuffer shapeCoordinatesArray = null; + IntBuffer shapeTypesArray = null; + + public void drawfillShape(CRenderer renderer, SunGraphics2D sg2d, GeneralPath gp, boolean isfill, boolean shouldApplyOffset) { + // System.err.println("--- drawfillShape"); + + if ((isfill) && (isCustomPaint(sg2d))) { + Rectangle bounds = gp.getBounds(); + setupGraphicsState(sg2d, kShape, bounds.x, bounds.y, bounds.width, bounds.height); + } else { + setupGraphicsState(sg2d, kShape, sg2d.font, 0, 0, fBounds.width, fBounds.height); + } + + int shapeLength = getPathLength(gp); + + if ((shapeCoordinatesArray == null) || (shapeCoordinatesArray.capacity() < (shapeLength * 6))) { + shapeCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a max of 6 + // coordinates + } + if ((shapeTypesArray == null) || (shapeTypesArray.capacity() < shapeLength)) { + shapeTypesArray = getBufferOfSize(shapeLength).asIntBuffer(); + } + + int windingRule = getPathCoordinates(gp, shapeCoordinatesArray, shapeTypesArray); + + renderer.doShape(this, shapeLength, shapeCoordinatesArray, shapeTypesArray, windingRule, isfill, shouldApplyOffset); + } + + public void blitImage(CRenderer renderer, SunGraphics2D sg2d, SurfaceData img, boolean fliph, boolean flipv, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, Color bgColor) { + // System.err.println("--- blitImage sx="+sx+", sy="+sy+", sw="+sw+", sh="+sh+", img="+img); + OSXOffScreenSurfaceData osxsd = (OSXOffScreenSurfaceData) img; + synchronized (osxsd.getLockObject()) { + int w = osxsd.bim.getWidth(); + int h = osxsd.bim.getHeight(); + + // the image itself can have outstanding graphics primitives that might need to be flushed + setupGraphicsState(sg2d, kImage, sg2d.font, 0, 0, fBounds.width, fBounds.height); + + // 04/06/04 cmc: radr://3612381 Graphics.drawImage ignores bgcolor parameter + if (bgColor != null) { + img = osxsd.getCopyWithBgColor(bgColor); + } + + renderer.doImage(this, img, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh); + } + } + + public interface CGContextDrawable { + public void drawIntoCGContext(final long cgContext); + } + + public void drawString(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, String str, double x, double y) { + // System.err.println("--- drawString str=\""+str+"\""); + // see <rdar://problem/3825795>. We don't want to call anything if the string is empty! + if (str.length() == 0) { return; } + + setupGraphicsState(sg2d, kString, sg2d.font, 0, 0, fBounds.width, fBounds.height); + renderer.doDrawString(this, nativeStrikePtr, str, x, y); + } + + public void drawGlyphs(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, GlyphVector gv, float x, float y) { + // System.err.println("--- drawGlyphs"); + setupGraphicsState(sg2d, kGlyphs, gv.getFont(), 0, 0, fBounds.width, fBounds.height); + renderer.doDrawGlyphs(this, nativeStrikePtr, gv, x, y); + } + + public void drawUnicodes(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, char unicodes[], int offset, int length, float x, float y) { + // System.err.println("--- drawUnicodes "+(new String(unicodes, offset, length))); + setupGraphicsState(sg2d, kUnicodes, sg2d.font, 0, 0, fBounds.width, fBounds.height); + if (length == 1) { + renderer.doOneUnicode(this, nativeStrikePtr, unicodes[offset], x, y); + } else { + renderer.doUnicodes(this, nativeStrikePtr, unicodes, offset, length, x, y); + } + } + + // used by copyArea: + + Rectangle srcCopyAreaRect = new Rectangle(); + Rectangle dstCopyAreaRect = new Rectangle(); + Rectangle finalCopyAreaRect = new Rectangle(); + Rectangle copyAreaBounds = new Rectangle(); + + void intersection(Rectangle r1, Rectangle r2, Rectangle r3) { + // this code is taken from Rectangle.java (modified to put results in r3) + int tx1 = r1.x; + int ty1 = r1.y; + long tx2 = tx1 + r1.width; + long ty2 = ty1 + r1.height; + + int rx1 = r2.x; + int ry1 = r2.y; + long rx2 = rx1 + r2.width; + long ry2 = ry1 + r2.height; + + if (tx1 < rx1) tx1 = rx1; + if (ty1 < ry1) ty1 = ry1; + if (tx2 > rx2) tx2 = rx2; + if (ty2 > ry2) ty2 = ry2; + + tx2 -= tx1; + ty2 -= ty1; + + // tx2,ty2 will never overflow (they will never be + // larger than the smallest of the two source w,h) + // they might underflow, though... + if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE; + if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE; + + r3.setBounds(tx1, ty1, (int) tx2, (int) ty2); + } + + /** + * Clips the copy area to the heavywieght bounds and returns the cliped rectangle. The tricky part here is the the + * passed arguments x, y are in the coordinate space of the sg2d/lightweight comp. In order to do the clipping we + * translate them to the coordinate space of the surface, and the returned clipped rectangle is in the coordinate + * space of the surface. + */ + protected Rectangle clipCopyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) { + // we need to clip against the heavyweight bounds + copyAreaBounds.setBounds(sg2d.devClip.getLoX(), sg2d.devClip.getLoY(), sg2d.devClip.getWidth(), sg2d.devClip.getHeight()); + + // put src rect into surface coordinate space + x += sg2d.transX; + y += sg2d.transY; + + // clip src rect + srcCopyAreaRect.setBounds(x, y, w, h); + intersection(srcCopyAreaRect, copyAreaBounds, srcCopyAreaRect); + if ((srcCopyAreaRect.width <= 0) || (srcCopyAreaRect.height <= 0)) { + // src rect outside bounds + return null; + } + + // clip dst rect + dstCopyAreaRect.setBounds(srcCopyAreaRect.x + dx, srcCopyAreaRect.y + dy, srcCopyAreaRect.width, srcCopyAreaRect.height); + intersection(dstCopyAreaRect, copyAreaBounds, dstCopyAreaRect); + if ((dstCopyAreaRect.width <= 0) || (dstCopyAreaRect.height <= 0)) { + // dst rect outside clip + return null; + } + + x = dstCopyAreaRect.x - dx; + y = dstCopyAreaRect.y - dy; + w = dstCopyAreaRect.width; + h = dstCopyAreaRect.height; + + finalCopyAreaRect.setBounds(x, y, w, h); + + return finalCopyAreaRect; + } + + // <rdar://3785539> We only need to mark dirty on screen surfaces. This method is + // marked as protected and it is intended for subclasses to override if they need to + // be notified when the surface is dirtied. See CPeerSurfaceData.markDirty() for implementation. + // We don't do anything for buffered images. + protected void markDirty(boolean markAsDirty) { + // do nothing by default + } + + // LazyDrawing optimization implementation: + + @Override + public boolean canRenderLCDText(SunGraphics2D sg2d) { + if (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY && + sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR && + sg2d.clipState <= SunGraphics2D.CLIP_RECTANGULAR && + // sg2d.surfaceData.getTransparency() == Transparency.OPAQUE && + // This last test is a workaround until we fix loop selection + // in the pipe validation + sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { return true; } + return false; /* for now - in the future we may want to search */ + } + + public static boolean IsSimpleColor(Object c) { + return ((c instanceof Color) || (c instanceof SystemColor) || (c instanceof javax.swing.plaf.ColorUIResource)); + } + + static { + if ((kColorPointerIndex % 2) != 0) { + System.err.println("kColorPointerIndex=" + kColorPointerIndex + " is NOT aligned for 64 bit"); + System.exit(0); + } + } +}
--- a/src/macosx/classes/sun/lwawt/LWComponentPeer.java Fri Sep 23 21:13:13 2011 +0400 +++ b/src/macosx/classes/sun/lwawt/LWComponentPeer.java Fri Sep 23 13:42:06 2011 -0700 @@ -47,12 +47,7 @@ import java.util.concurrent.atomic.AtomicBoolean; -import sun.awt.AWTAccessor; -import sun.awt.CausedFocusEvent; -import sun.awt.PaintEventDispatcher; -import sun.awt.RepaintArea; -import sun.awt.SunToolkit; -import sun.awt.AppContext; +import sun.awt.*; import sun.awt.event.IgnorePaintEvent; @@ -659,7 +654,15 @@ @Override public void print(Graphics g) { - // TODO: not implemented + Component c = getTarget(); + if (c instanceof Container) { + SunGraphicsCallback.PrintHeavyweightComponentsCallback.getInstance(). + runComponents(((Container)getTarget()).getComponents(), g, + SunGraphicsCallback.LIGHTWEIGHTS | + SunGraphicsCallback.HEAVYWEIGHTS); + } else { + c.print(g); + } } @Override
--- a/src/macosx/classes/sun/lwawt/LWToolkit.java Fri Sep 23 21:13:13 2011 +0400 +++ b/src/macosx/classes/sun/lwawt/LWToolkit.java Fri Sep 23 13:42:06 2011 -0700 @@ -27,17 +27,17 @@ import java.awt.*; import java.awt.List; -import java.awt.datatransfer.Clipboard; -import java.awt.dnd.DragGestureEvent; -import java.awt.dnd.peer.DragSourceContextPeer; -import java.awt.image.ColorModel; +import java.awt.datatransfer.*; +import java.awt.dnd.*; +import java.awt.dnd.peer.*; +import java.awt.image.*; import java.awt.peer.*; import java.security.*; import java.util.*; -import sun.lwawt.macosx.CDragSourceContextPeer; - import sun.awt.*; +import sun.lwawt.macosx.*; +import sun.print.*; public abstract class LWToolkit extends SunToolkit implements Runnable { @@ -232,8 +232,20 @@ return createDelegatedPeer(target, delegate); } + + CPrinterDialogPeer createCPrinterDialog(CPrinterDialog target) { + PlatformWindow delegate = createPlatformWindow(LWWindowPeer.PeerType.DIALOG); + CPrinterDialogPeer peer = new CPrinterDialogPeer(target, delegate); + targetCreatedPeer(target, peer); + return peer; + } + @Override public DialogPeer createDialog(Dialog target) { + if (target instanceof CPrinterDialog) { + return createCPrinterDialog((CPrinterDialog)target); + } + PlatformWindow delegate = createPlatformWindow(LWWindowPeer.PeerType.DIALOG); return createDelegatedPeer(target, delegate); } @@ -408,9 +420,22 @@ return new LWMouseInfoPeer(); } - @Override - public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) { - throw new RuntimeException("not implemented"); + public PrintJob getPrintJob(Frame frame, String doctitle, Properties props) { + return getPrintJob(frame, doctitle, null, null); + } + + public PrintJob getPrintJob(Frame frame, String doctitle, JobAttributes jobAttributes, PageAttributes pageAttributes) { + if (GraphicsEnvironment.isHeadless()) { + throw new IllegalArgumentException(); + } + + PrintJob2D printJob = new PrintJob2D(frame, doctitle, jobAttributes, pageAttributes); + + if (printJob.printDialog() == false) { + printJob = null; + } + + return printJob; } @Override
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CPrinterDevice.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + +import java.awt.*; + +public class CPrinterDevice extends GraphicsDevice { + GraphicsConfiguration gc; + + public CPrinterDevice(CPrinterGraphicsConfig gc) { + this.gc = gc; + } + + /** + * Returns the type of this <code>GraphicsDevice</code>. + * @return the type of this <code>GraphicsDevice</code>, which can + * either be TYPE_RASTER_SCREEN, TYPE_PRINTER or TYPE_IMAGE_BUFFER. + * @see #TYPE_RASTER_SCREEN + * @see #TYPE_PRINTER + * @see #TYPE_IMAGE_BUFFER + */ + public int getType() { + return GraphicsDevice.TYPE_PRINTER; + } + + /** + * Returns the identification string associated with this + * <code>GraphicsDevice</code>. + * @return a <code>String</code> that is the identification + * of this <code>GraphicsDevice</code>. + */ + public String getIDstring() { + return ("Printer"); + } + + /** + * Returns all of the <code>GraphicsConfiguration</code> + * objects associated with this <code>GraphicsDevice</code>. + * @return an array of <code>GraphicsConfiguration</code> + * objects that are associated with this + * <code>GraphicsDevice</code>. + */ + public GraphicsConfiguration[] getConfigurations() { + return new GraphicsConfiguration[] { gc }; + } + + /** + * Returns the default <code>GraphicsConfiguration</code> + * associated with this <code>GraphicsDevice</code>. + * @return the default <code>GraphicsConfiguration</code> + * of this <code>GraphicsDevice</code>. + */ + public GraphicsConfiguration getDefaultConfiguration() { + return gc; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CPrinterDialog.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + +import java.awt.*; + +public abstract class CPrinterDialog extends Dialog { + private final CPrinterJob fPrinterJob; // used from native + + CPrinterDialog(Frame parent, CPrinterJob printerJob) { + super(parent, true); + fPrinterJob = printerJob; + setLayout(null); + } + + private boolean retval = false; + + public void setRetVal(boolean ret) { + retval = ret; + } + + public boolean getRetVal() { + return retval; + } + + protected abstract boolean showDialog(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CPrinterDialogPeer.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + +import java.awt.*; +import java.awt.dnd.*; + +import sun.lwawt.*; + +public class CPrinterDialogPeer extends LWWindowPeer { + static { + // AWT has to be initialized for the native code to function correctly. + Toolkit.getDefaultToolkit(); + } + + Component fTarget; + + public CPrinterDialogPeer(CPrinterDialog target, PlatformWindow delegate) { + super(target, delegate); + //super(target); + fTarget = target; + super.initialize(); + } + + protected void disposeImpl() { + LWCToolkit.targetDisposedPeer(fTarget, this); + } + + public void setVisible(boolean visible) { + if (visible) { + new Thread(new Runnable() { + public void run() { + CPrinterDialog printerDialog = (CPrinterDialog)fTarget; + printerDialog.setRetVal(printerDialog.showDialog()); + printerDialog.setVisible(false); + } + }).start(); + } + } + + // unused methods. + public void toFront() {} + public void toBack() {} + public void setResizable(boolean resizable) {} + public void setEnabled(boolean enable) {} + public void setBounds(int x, int y, int width, int height) {} + public boolean handleEvent(Event e) { return false; } + public void setForeground(Color c) {} + public void setBackground(Color c) {} + public void setFont(Font f) {} + public boolean requestFocus(boolean temporary, boolean focusedWindowChangeAllowed) { + return false; + } + void start() {} + void invalidate(int x, int y, int width, int height) {} + public void addDropTarget(DropTarget dt) {} + public void removeDropTarget(DropTarget dt) {} + + // 1.5 peer method + public boolean isRestackSupported() { + return false; + } + + // 1.6 peer method + public void setAlwaysOnTop(boolean value) { + // no-op, since we just show the native print dialog + } + + // 1.6 peer method + public void updateMinimumSize() {} + + // 1.6 peer method + public void setModalBlocked(Dialog blocker, boolean blocked) { + // I don't think we care since this is a native dialog + } + + // 1.6 peer method + public void updateFocusableWindowState() {} +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CPrinterGraphics.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + +import java.awt.*; +import java.awt.image.*; +import java.awt.print.*; +import sun.print.*; + +public class CPrinterGraphics extends ProxyGraphics2D { + // NOTE: This is a ProxyGraphics2D, and not a PathGraphics. However + // the RasterPrinterJob, upon which CPrinterJob is based, refers to + // PathGraphics. However, this is not a code path that will be + // encountered by CPrinterJob/CPrinterGraphics. This is because + // CPrinterGraphics wraps a SunGraphics2D that has a OSXSurfaceData + // based CPrinterSurfaceData. It can do "path graphics" because it + // is based upon CoreGraphics. See WPathGraphics and PSPathGraphics. + + public CPrinterGraphics(Graphics2D graphics, PrinterJob printerJob) { + super(graphics, printerJob); + } + + public boolean drawImage(Image img, int x, int y, + Color bgcolor, + ImageObserver observer) { + // ProxyGraphics2D works around a problem that shouldn't be + // a problem with CPrinterSurfaceData (and the decision method, + // needToCopyBgColorImage, is private instead of protected!) + return getDelegate().drawImage(img, x, y, bgcolor, observer); + } + + public boolean drawImage(Image img, int x, int y, + int width, int height, + Color bgcolor, + ImageObserver observer) { + // ProxyGraphics2D works around a problem that shouldn't be + // a problem with CPrinterSurfaceData (and the decision method, + // needToCopyBgColorImage, is private instead of protected!) + return getDelegate().drawImage(img, x, y, width, height, bgcolor, observer); + } + + public boolean drawImage(Image img, + int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + Color bgcolor, + ImageObserver observer) { + // ProxyGraphics2D works around a problem that shouldn't be + // a problem with CPrinterSurfaceData (and the decision method, + // needToCopyBgColorImage, is private instead of protected!) + return getDelegate().drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CPrinterGraphicsConfig.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.awt.print.*; + +public class CPrinterGraphicsConfig extends GraphicsConfiguration { + public static CPrinterGraphicsConfig getConfig(PageFormat pf) { + return new CPrinterGraphicsConfig(pf); + } + + GraphicsDevice gd; + PageFormat pf; + + public CPrinterGraphicsConfig(PageFormat pf) { + this.gd = new CPrinterDevice(this); + this.pf = pf; + } + + public PageFormat getPageFormat() { + return pf; + } + + /** + * Returns the {@link GraphicsDevice} associated with this + * <code>GraphicsConfiguration</code>. + * @return a <code>GraphicsDevice</code> object that is + * associated with this <code>GraphicsConfiguration</code>. + */ + public GraphicsDevice getDevice() { + return gd; + } + + /** + * Returns a {@link BufferedImage} with a data layout and color model + * compatible with this <code>GraphicsConfiguration</code>. This + * method has nothing to do with memory-mapping + * a device. The returned <code>BufferedImage</code> has + * a layout and color model that is closest to this native device + * configuration and can therefore be optimally blitted to this + * device. + * @param width the width of the returned <code>BufferedImage</code> + * @param height the height of the returned <code>BufferedImage</code> + * @return a <code>BufferedImage</code> whose data layout and color + * model is compatible with this <code>GraphicsConfiguration</code>. + */ + public BufferedImage createCompatibleImage(int width, int height) { + return createCompatibleImage(width, height, Transparency.OPAQUE); + } + + /** + * Returns a {@link VolatileImage} with a data layout and color model + * compatible with this <code>GraphicsConfiguration</code>. + * The returned <code>VolatileImage</code> + * may have data that is stored optimally for the underlying graphics + * device and may therefore benefit from platform-specific rendering + * acceleration. + * @param width the width of the returned <code>VolatileImage</code> + * @param height the height of the returned <code>VolatileImage</code> + * @return a <code>VolatileImage</code> whose data layout and color + * model is compatible with this <code>GraphicsConfiguration</code>. + * @see Component#createVolatileImage(int, int) + */ + public VolatileImage createCompatibleVolatileImage(int width, int height) { + return createCompatibleVolatileImage(width, height, Transparency.OPAQUE); + } + + // empty implementation (this should not be called) + public VolatileImage createCompatibleVolatileImage(int width, int height, int transparency) { + return null; + } + + /** + * Returns a <code>BufferedImage</code> that supports the specified + * transparency and has a data layout and color model + * compatible with this <code>GraphicsConfiguration</code>. This + * method has nothing to do with memory-mapping + * a device. The returned <code>BufferedImage</code> has a layout and + * color model that can be optimally blitted to a device + * with this <code>GraphicsConfiguration</code>. + * @param width the width of the returned <code>BufferedImage</code> + * @param height the height of the returned <code>BufferedImage</code> + * @param transparency the specified transparency mode + * @return a <code>BufferedImage</code> whose data layout and color + * model is compatible with this <code>GraphicsConfiguration</code> + * and also supports the specified transparency. + * @see Transparency#OPAQUE + * @see Transparency#BITMASK + * @see Transparency#TRANSLUCENT + */ + public BufferedImage createCompatibleImage(int width, int height, int transparency) { + //+++gdb what to do? + return null; + } + + /** + * Returns the {@link ColorModel} associated with this + * <code>GraphicsConfiguration</code>. + * @return a <code>ColorModel</code> object that is associated with + * this <code>GraphicsConfiguration</code>. + */ + public ColorModel getColorModel() { + return getColorModel(Transparency.OPAQUE); + } + + /** + * Returns the <code>ColorModel</code> associated with this + * <code>GraphicsConfiguration</code> that supports the specified + * transparency. + * @param transparency the specified transparency mode + * @return a <code>ColorModel</code> object that is associated with + * this <code>GraphicsConfiguration</code> and supports the + * specified transparency. + */ + public ColorModel getColorModel(int transparency) { + return ColorModel.getRGBdefault(); + } + + /** + * Returns the default {@link AffineTransform} for this + * <code>GraphicsConfiguration</code>. This + * <code>AffineTransform</code> is typically the Identity transform + * for most normal screens. The default <code>AffineTransform</code> + * maps coordinates onto the device such that 72 user space + * coordinate units measure approximately 1 inch in device + * space. The normalizing transform can be used to make + * this mapping more exact. Coordinates in the coordinate space + * defined by the default <code>AffineTransform</code> for screen and + * printer devices have the origin in the upper left-hand corner of + * the target region of the device, with X coordinates + * increasing to the right and Y coordinates increasing downwards. + * For image buffers not associated with a device, such as those not + * created by <code>createCompatibleImage</code>, + * this <code>AffineTransform</code> is the Identity transform. + * @return the default <code>AffineTransform</code> for this + * <code>GraphicsConfiguration</code>. + */ + public AffineTransform getDefaultTransform() { + return new AffineTransform(); + } + + /** + * + * Returns a <code>AffineTransform</code> that can be concatenated + * with the default <code>AffineTransform</code> + * of a <code>GraphicsConfiguration</code> so that 72 units in user + * space equals 1 inch in device space. + * <p> + * For a particular {@link Graphics2D}, g, one + * can reset the transformation to create + * such a mapping by using the following pseudocode: + * <pre> + * GraphicsConfiguration gc = g.getGraphicsConfiguration(); + * + * g.setTransform(gc.getDefaultTransform()); + * g.transform(gc.getNormalizingTransform()); + * </pre> + * Note that sometimes this <code>AffineTransform</code> is identity, + * such as for printers or metafile output, and that this + * <code>AffineTransform</code> is only as accurate as the information + * supplied by the underlying system. For image buffers not + * associated with a device, such as those not created by + * <code>createCompatibleImage</code>, this + * <code>AffineTransform</code> is the Identity transform + * since there is no valid distance measurement. + * @return an <code>AffineTransform</code> to concatenate to the + * default <code>AffineTransform</code> so that 72 units in user + * space is mapped to 1 inch in device space. + */ + public AffineTransform getNormalizingTransform() { + return new AffineTransform(); + } + + /** + * Returns the bounds of the <code>GraphicsConfiguration</code> + * in the device coordinates. In a multi-screen environment + * with a virtual device, the bounds can have negative X + * or Y origins. + * @return the bounds of the area covered by this + * <code>GraphicsConfiguration</code>. + * @since 1.3 + */ + public Rectangle getBounds() { + return new Rectangle(0, 0, (int)pf.getWidth(), (int)pf.getHeight()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CPrinterJob.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.print.*; + +import javax.print.*; +import javax.print.attribute.PrintRequestAttributeSet; + +import sun.java2d.*; +import sun.print.*; + +public class CPrinterJob extends RasterPrinterJob { + // NOTE: This uses RasterPrinterJob as a base, but it doesn't use + // all of the RasterPrinterJob functions. RasterPrinterJob will + // break down printing to pieces that aren't necessary under MacOSX + // printing, such as controlling the # of copies and collating. These + // are handled by the native printing. RasterPrinterJob is kept for + // future compatibility and the state keeping that it handles. + + private static String sShouldNotReachHere = "Should not reach here."; + + private boolean noDefaultPrinter = false; + + private static Font defaultFont; + + // This is the NSPrintInfo for this PrinterJob. Protect multi thread + // access to it. It is used by the pageDialog, jobDialog, and printLoop. + // This way the state of these items is shared across these calls. + // PageFormat data is passed in and set on the fNSPrintInfo on a per call + // basis. + private long fNSPrintInfo = -1; + private Object fNSPrintInfoLock = new Object(); + + static { + // AWT has to be initialized for the native code to function correctly. + Toolkit.getDefaultToolkit(); + } + + /** + * Presents a dialog to the user for changing the properties of + * the print job. + * This method will display a native dialog if a native print + * service is selected, and user choice of printers will be restricted + * to these native print services. + * To present the cross platform print dialog for all services, + * including native ones instead use + * <code>printDialog(PrintRequestAttributeSet)</code>. + * <p> + * PrinterJob implementations which can use PrintService's will update + * the PrintService for this PrinterJob to reflect the new service + * selected by the user. + * @return <code>true</code> if the user does not cancel the dialog; + * <code>false</code> otherwise. + * @exception HeadlessException if GraphicsEnvironment.isHeadless() + * returns true. + * @see java.awt.GraphicsEnvironment#isHeadless + */ + public boolean printDialog() throws HeadlessException { + if (GraphicsEnvironment.isHeadless()) { + throw new HeadlessException(); + } + + if (noDefaultPrinter) { + return false; + } + + return jobSetup(getPageable(), checkAllowedToPrintToFile()); + } + + /** + * Displays a dialog that allows modification of a + * <code>PageFormat</code> instance. + * The <code>page</code> argument is used to initialize controls + * in the page setup dialog. + * If the user cancels the dialog then this method returns the + * original <code>page</code> object unmodified. + * If the user okays the dialog then this method returns a new + * <code>PageFormat</code> object with the indicated changes. + * In either case, the original <code>page</code> object is + * not modified. + * @param page the default <code>PageFormat</code> presented to the + * user for modification + * @return the original <code>page</code> object if the dialog + * is cancelled; a new <code>PageFormat</code> object + * containing the format indicated by the user if the + * dialog is acknowledged. + * @exception HeadlessException if GraphicsEnvironment.isHeadless() + * returns true. + * @see java.awt.GraphicsEnvironment#isHeadless + * @since 1.2 + */ + public PageFormat pageDialog(PageFormat page) throws HeadlessException { + if (GraphicsEnvironment.isHeadless()) { + throw new HeadlessException(); + } + + if (noDefaultPrinter) { + return page; + } + + PageFormat pageClone = (PageFormat) page.clone(); + boolean doIt = pageSetup(pageClone, null); + return doIt ? pageClone : page; + } + + /** + * Clones the <code>PageFormat</code> argument and alters the + * clone to describe a default page size and orientation. + * @param page the <code>PageFormat</code> to be cloned and altered + * @return clone of <code>page</code>, altered to describe a default + * <code>PageFormat</code>. + */ + public PageFormat defaultPage(PageFormat page) { + PageFormat newPage = (PageFormat)page.clone(); + getDefaultPage(newPage); + return newPage; + } + + protected void setAttributes(PrintRequestAttributeSet attributes) throws PrinterException { + super.setAttributes(attributes); + + if (attributes == null) { + return; + } + + // See if this has an NSPrintInfo in it. + NSPrintInfo nsPrintInfo = (NSPrintInfo)attributes.get(NSPrintInfo.class); + if (nsPrintInfo != null) { + fNSPrintInfo = nsPrintInfo.getValue(); + } + } + + volatile boolean onEventThread; + + private void completePrintLoop() { + Runnable r = new Runnable() { public void run() { + synchronized(this) { + performingPrinting = false; + } + }}; + + if (onEventThread) { + try { EventQueue.invokeAndWait(r); } catch (Exception e) { e.printStackTrace(); } + } else { + r.run(); + } + } + + + public void print(PrintRequestAttributeSet attributes) throws PrinterException { + // NOTE: Some of this code is copied from RasterPrinterJob. + +/* + // this code uses javax.print APIs + // this will make it print directly to the printer + // this will not work if the user clicks on the "Preview" button + PrintService psvc = getPrintService(); + spoolToService(psvc, attributes); + return; +*/ + + setAttributes(attributes); + + /* Get the range of pages we are to print. If the + * last page to print is unknown, then we print to + * the end of the document. Note that firstPage + * and lastPage are 0 based page indices. + */ + int numPages = mDocument.getNumberOfPages(); + + int firstPage = getFirstPage(); + int lastPage = getLastPage(); + if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES) { + int totalPages = mDocument.getNumberOfPages(); + if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) { + lastPage = mDocument.getNumberOfPages() - 1; + } + } + + try { + synchronized (this) { + performingPrinting = true; + userCancelled = false; + } + + if (EventQueue.isDispatchThread()) { + // This is an AWT EventQueue, and this print rendering loop needs to block it. + + onEventThread = true; + + try { + // Fire off the print rendering loop on the AppKit thread, and don't have + // it wait and block this thread. + if (printLoop(false, firstPage, lastPage)) { + // Fire off the EventConditional that will what until the condition is met, + // but will still process AWTEvent's as they occur. + new EventDispatchAccess() { + public boolean evaluate() { + return performingPrinting; + } + }.pumpEventsAndWait(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else { + // Fire off the print rendering loop on the AppKit, and block this thread + // until it is done. + // But don't actually block... we need to come back here! + onEventThread = false; + + try { + printLoop(true, firstPage, lastPage); + } catch (Exception e) { + e.printStackTrace(); + } + } + } finally { + synchronized (this) { + // NOTE: Native code shouldn't allow exceptions out while + // printing. They should cancel the print loop. + performingPrinting = false; + notify(); + } + } + + // Normalize the collated, # copies, numPages, first/last pages. Need to + // make note of pageRangesAttr. + + // Set up NSPrintInfo with the java settings (PageFormat & Paper). + + // Create an NSView for printing. Have knowsPageRange return YES, and give the correct + // range, or MAX? if unknown. Have rectForPage do a peekGraphics check before returning + // the rectangle. Have drawRect do the real render of the page. Have printJobTitle do + // the right thing. + + // Call NSPrintOperation, it will call NSView.drawRect: for each page. + + // NSView.drawRect: will create a CPrinterGraphics with the current CGContextRef, and then + // pass this Graphics onto the Printable with the appropriate PageFormat and index. + + // Need to be able to cancel the NSPrintOperation (using code from RasterPrinterJob, be + // sure to initialize userCancelled and performingPrinting member variables). + + // Extensions available from AppKit: Print to PDF or EPS file! + } + + /** + * Returns the resolution in dots per inch across the width + * of the page. + */ + protected double getXRes() { + // NOTE: This is not used in the CPrinterJob code path. + return 0; + } + + /** + * Returns the resolution in dots per inch down the height + * of the page. + */ + protected double getYRes() { + // NOTE: This is not used in the CPrinterJob code path. + return 0; + } + + /** + * Must be obtained from the current printer. + * Value is in device pixels. + * Not adjusted for orientation of the paper. + */ + protected double getPhysicalPrintableX(Paper p) { + // NOTE: This is not used in the CPrinterJob code path. + return 0; + } + + /** + * Must be obtained from the current printer. + * Value is in device pixels. + * Not adjusted for orientation of the paper. + */ + protected double getPhysicalPrintableY(Paper p) { + // NOTE: This is not used in the CPrinterJob code path. + return 0; + } + + /** + * Must be obtained from the current printer. + * Value is in device pixels. + * Not adjusted for orientation of the paper. + */ + protected double getPhysicalPrintableWidth(Paper p) { + // NOTE: This is not used in the CPrinterJob code path. + return 0; + } + + /** + * Must be obtained from the current printer. + * Value is in device pixels. + * Not adjusted for orientation of the paper. + */ + protected double getPhysicalPrintableHeight(Paper p) { + // NOTE: This is not used in the CPrinterJob code path. + return 0; + } + + /** + * Must be obtained from the current printer. + * Value is in device pixels. + * Not adjusted for orientation of the paper. + */ + protected double getPhysicalPageWidth(Paper p) { + // NOTE: This is not used in the CPrinterJob code path. + return 0; + } + + /** + * Must be obtained from the current printer. + * Value is in device pixels. + * Not adjusted for orientation of the paper. + */ + protected double getPhysicalPageHeight(Paper p) { + // NOTE: This is not used in the CPrinterJob code path. + return 0; + } + + /** + * Begin a new page. This call's Window's + * StartPage routine. + */ + protected void startPage(PageFormat format, Printable painter, int index) throws PrinterException { + // NOTE: This is not used in the CPrinterJob code path. + throw new PrinterException(sShouldNotReachHere); + } + + /** + * End a page. + */ + protected void endPage(PageFormat format, Printable painter, int index) throws PrinterException { + // NOTE: This is not used in the CPrinterJob code path. + throw new PrinterException(sShouldNotReachHere); + } + + /** + * Prints the contents of the array of ints, 'data' + * to the current page. The band is placed at the + * location (x, y) in device coordinates on the + * page. The width and height of the band is + * specified by the caller. + */ + protected void printBand(byte[] data, int x, int y, int width, int height) throws PrinterException { + // NOTE: This is not used in the CPrinterJob code path. + throw new PrinterException(sShouldNotReachHere); + } + + /** + * Called by the print() method at the start of + * a print job. + */ + protected void startDoc() throws PrinterException { + // NOTE: This is not used in the CPrinterJob code path. + throw new PrinterException(sShouldNotReachHere); + } + + /** + * Called by the print() method at the end of + * a print job. + */ + protected void endDoc() throws PrinterException { + // NOTE: This is not used in the CPrinterJob code path. + throw new PrinterException(sShouldNotReachHere); + } + + /* Called by cancelDoc */ + protected native void abortDoc(); + + /** + * Displays the page setup dialog placing the user's + * settings into 'page'. + */ + public boolean pageSetup(PageFormat page, Printable painter) { + CPrinterDialog printerDialog = new CPrinterPageDialog(null, this, page, painter); + printerDialog.setVisible(true); + boolean result = printerDialog.getRetVal(); + printerDialog.dispose(); + return result; + } + + /** + * Displays the print dialog and records the user's settings + * into this object. Return false if the user cancels the + * dialog. + * If the dialog is to use a set of attributes, useAttributes is true. + */ + private boolean jobSetup(Pageable doc, boolean allowPrintToFile) { + CPrinterDialog printerDialog = new CPrinterJobDialog(null, this, doc, allowPrintToFile); + printerDialog.setVisible(true); + boolean result = printerDialog.getRetVal(); + printerDialog.dispose(); + return result; + } + + /** + * Alters the orientation and Paper to match defaults obtained + * from a printer. + */ + private native void getDefaultPage(PageFormat page); + + /** + * validate the paper size against the current printer. + */ + protected native void validatePaper(Paper origPaper, Paper newPaper ); + + // The following methods are CPrinterJob specific. + + protected void finalize() { + if (fNSPrintInfo != -1) { + dispose(fNSPrintInfo); + } + } + + private native long createNSPrintInfo(); + private native void dispose(long printInfo); + + private long getNSPrintInfo() { + // This is called from the native side. + synchronized (fNSPrintInfoLock) { + if (fNSPrintInfo == -1) { + fNSPrintInfo = createNSPrintInfo(); + } + return fNSPrintInfo; + } + } + + private native boolean printLoop(boolean waitUntilDone, int firstPage, int lastPage) throws PrinterException; + + private PageFormat getPageFormat(int pageIndex) { + // This is called from the native side. + PageFormat page; + try { + page = getPageable().getPageFormat(pageIndex); + } catch (Exception e) { + return null; + } + return page; + } + + private Printable getPrintable(int pageIndex) { + // This is called from the native side. + Printable painter; + try { + painter = getPageable().getPrintable(pageIndex); + } catch (Exception e) { + return null; + } + return painter; + } + + private String getPrinterName(){ + // This is called from the native side. + PrintService service = getPrintService(); + if (service == null) return null; + return service.getName(); + } + + private void setPrinterServiceFromNative(String printerName) { + // This is called from the native side. + PrintService[] services = PrintServiceLookup.lookupPrintServices(DocFlavor.SERVICE_FORMATTED.PAGEABLE, null); + + for (int i = 0; i < services.length; i++) { + PrintService service = services[i]; + + if (printerName.equals(service.getName())) { + try { + setPrintService(service); + } catch (PrinterException e) { + // ignored + } + return; + } + } + } + + private Rectangle2D getPageFormatArea(PageFormat page) { + Rectangle2D.Double pageFormatArea = + new Rectangle2D.Double(page.getImageableX(), + page.getImageableY(), + page.getImageableWidth(), + page.getImageableHeight()); + return pageFormatArea; + } + + private boolean cancelCheck() { + // This is called from the native side. + + // This is used to avoid deadlock + // We would like to just call if isCancelled(), + // but that will block the AppKit thread against whomever is holding the synchronized lock + boolean cancelled = (performingPrinting && userCancelled); + if (cancelled) { + try { + LWCToolkit.invokeLater(new Runnable() { public void run() { + try { + cancelDoc(); + } catch (PrinterAbortException pae) { + // no-op, let the native side handle it + } + }}, null); + } catch (java.lang.reflect.InvocationTargetException ite) {} + } + return cancelled; + } + + private PeekGraphics createFirstPassGraphics(PrinterJob printerJob, PageFormat page) { + // This is called from the native side. + BufferedImage bimg = new BufferedImage((int)Math.round(page.getWidth()), (int)Math.round(page.getHeight()), BufferedImage.TYPE_INT_ARGB_PRE); + PeekGraphics peekGraphics = createPeekGraphics(bimg.createGraphics(), printerJob); + Rectangle2D pageFormatArea = getPageFormatArea(page); + initPrinterGraphics(peekGraphics, pageFormatArea); + return peekGraphics; + } + + private void printToPathGraphics( final PeekGraphics graphics, // Always an actual PeekGraphics + final PrinterJob printerJob, // Always an actual CPrinterJob + final Printable painter, // Client class + final PageFormat page, // Client class + final int pageIndex, + final long context) throws PrinterException { + // This is called from the native side. + Runnable r = new Runnable() { public void run() { + try { + SurfaceData sd = CPrinterSurfaceData.createData(page, context); // Just stores page into an ivar + if (defaultFont == null) { + defaultFont = new Font("Dialog", Font.PLAIN, 12); + } + Graphics2D delegate = new SunGraphics2D(sd, Color.black, Color.white, defaultFont); + + Graphics2D pathGraphics = new CPrinterGraphics(delegate, printerJob); // Just stores delegate into an ivar + Rectangle2D pageFormatArea = getPageFormatArea(page); + initPrinterGraphics(pathGraphics, pageFormatArea); + painter.print(pathGraphics, page, pageIndex); + delegate.dispose(); + delegate = null; + } catch (PrinterException pe) { throw new java.lang.reflect.UndeclaredThrowableException(pe); } + }}; + + if (onEventThread) { + try { EventQueue.invokeAndWait(r); + } catch (java.lang.reflect.InvocationTargetException ite) { + Throwable te = (Throwable)ite.getTargetException(); + if (te instanceof PrinterException) throw (PrinterException)te; + else te.printStackTrace(); + } catch (Exception e) { e.printStackTrace(); } + } else { + r.run(); + } + + } + + // Returns either 1. an array of 3 object (PageFormat, Printable, PeekGraphics) or 2. null + private Object[] getPageformatPrintablePeekgraphics(final int pageIndex) { + final Object[] ret = new Object[3]; + final PrinterJob printerJob = this; + + Runnable r = new Runnable() { public void run() { synchronized(ret) { + try { + Pageable pageable = getPageable(); + PageFormat pageFormat = pageable.getPageFormat(pageIndex); + if (pageFormat != null) { + Printable printable = pageable.getPrintable(pageIndex); + if (printable != null) { + BufferedImage bimg = new BufferedImage((int)Math.round(pageFormat.getWidth()), (int)Math.round(pageFormat.getHeight()), BufferedImage.TYPE_INT_ARGB_PRE); + PeekGraphics peekGraphics = createPeekGraphics(bimg.createGraphics(), printerJob); + Rectangle2D pageFormatArea = getPageFormatArea(pageFormat); + initPrinterGraphics(peekGraphics, pageFormatArea); + + // Do the assignment here! + ret[0] = pageFormat; + ret[1] = printable; + ret[2] = peekGraphics; + } + } + } catch (Exception e) {} // Original code bailed on any exception + }}}; + + if (onEventThread) { + try { EventQueue.invokeAndWait(r); } catch (Exception e) { e.printStackTrace(); } + } else { + r.run(); + } + + synchronized(ret) { + if (ret[2] != null) + return ret; + return null; + } + } + + private Rectangle2D printAndGetPageFormatArea(final Printable printable, final Graphics graphics, final PageFormat pageFormat, final int pageIndex) { + final Rectangle2D[] ret = new Rectangle2D[1]; + + Runnable r = new Runnable() { public void run() { synchronized(ret) { + try { + int pageResult = printable.print(graphics, pageFormat, pageIndex); + if (pageResult != Printable.NO_SUCH_PAGE) { + ret[0] = getPageFormatArea(pageFormat); + } + } catch (Exception e) {} // Original code bailed on any exception + }}}; + + if (onEventThread) { + try { EventQueue.invokeAndWait(r); } catch (Exception e) { e.printStackTrace(); } + } else { + r.run(); + } + + synchronized(ret) { return ret[0]; } + } + + // upcall from native + private static void detachPrintLoop(final long target, final long arg) { + new Thread() { public void run() { + _safePrintLoop(target, arg); + }}.start(); + } + private static native void _safePrintLoop(long target, long arg); + + @Override + protected void startPage(PageFormat arg0, Printable arg1, int arg2, boolean arg3) throws PrinterException { + // TODO Auto-generated method stub + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CPrinterJobDialog.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + + +import java.awt.*; +import java.awt.print.*; + +public class CPrinterJobDialog extends CPrinterDialog { + private Pageable fPageable; + private boolean fAllowPrintToFile; + + CPrinterJobDialog(Frame parent, CPrinterJob printerJob, Pageable doc, boolean allowPrintToFile) { + super(parent, printerJob); + fPageable = doc; + fAllowPrintToFile = allowPrintToFile; + } + + protected native boolean showDialog(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CPrinterPageDialog.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + + +import java.awt.*; +import java.awt.print.*; + +public class CPrinterPageDialog extends CPrinterDialog { + private PageFormat fPage; + private Printable fPainter; + + CPrinterPageDialog(Frame parent, CPrinterJob printerJob, PageFormat page, Printable painter) { + super(parent, printerJob); + fPage = page; + fPainter = painter; + } + + protected native boolean showDialog(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CPrinterSurfaceData.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + +import java.awt.*; +import java.awt.image.*; +import java.awt.print.PageFormat; +import java.nio.ByteBuffer; + +import sun.java2d.*; +import sun.java2d.loops.SurfaceType; + +public class CPrinterSurfaceData extends OSXSurfaceData{ + public static final String DESC_INT_RGB_PQ = "Integer RGB Printer Quartz"; +// public static final String DESC_INT_ARGB_PQ = "Integer ARGB Printer Quartz"; + +// public static final SurfaceType IntArgbPQ = SurfaceType.IntArgb.deriveSubType(DESC_INT_ARGB_PQ); + public static final SurfaceType IntRgbPQ = SurfaceType.IntRgb.deriveSubType(DESC_INT_RGB_PQ); + + public static SurfaceData createData(PageFormat pf, long context) { + return new CPrinterSurfaceData(CPrinterGraphicsConfig.getConfig(pf), context); + } + + public CPrinterSurfaceData(GraphicsConfiguration gc, long context) { + super(IntRgbPQ, gc.getColorModel(), gc, gc.getBounds()); + initOps(context, this.fGraphicsStates, this.fGraphicsStatesObject, gc.getBounds().width, gc.getBounds().height); + } + + public SurfaceData getReplacement() { + return this; + } + + private native void initOps(long context, ByteBuffer byteParameters, Object[] objectParameters, int width, int height); + + public void enableFlushing() { + _flush(); + } + native void _flush(); + + public Object getDestination() { + // this should never get called for the printer surface (see BufferStrategyPaintManager for one case of usage) + return null; + } + + public Raster getRaster(int x, int y, int w, int h) { + BufferedImage dstImage = new BufferedImage(x + w, y + h, BufferedImage.TYPE_INT_ARGB_PRE); + return dstImage.getRaster(); + } + + public BufferedImage copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, BufferedImage dstImage) { + // create the destination image if needed + if (dstImage == null) { + dstImage = getDeviceConfiguration().createCompatibleImage(w, h); + } + + // copy + Graphics g = dstImage.createGraphics(); + BufferedImage thisImage = getCompositingImage(w, h); + g.drawImage(thisImage, 0, 0, w, h, x, y, x+w, y+h, null); + g.dispose(); + + return dstImage; + } + + public boolean xorSurfacePixels(SunGraphics2D sg2d, BufferedImage srcPixels, int x, int y, int w, int h, int colorXOR) { + throw new InternalError("not implemented yet"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/CTextPipe.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + + +import java.awt.*; +import java.awt.font.*; + +import sun.awt.*; +import sun.font.*; +import sun.java2d.*; +import sun.java2d.loops.*; +import sun.java2d.pipe.*; + +public class CTextPipe implements TextPipe { + public native void doDrawString(SurfaceData sData, long nativeStrikePtr, String s, double x, double y); + public native void doDrawGlyphs(SurfaceData sData, long nativeStrikePtr, GlyphVector gV, float x, float y); + public native void doUnicodes(SurfaceData sData, long nativeStrikePtr, char unicodes[], int offset, int length, float x, float y); + public native void doOneUnicode(SurfaceData sData, long nativeStrikePtr, char aUnicode, float x, float y); + + long getNativeStrikePtr(final SunGraphics2D sg2d) { + final FontStrike fontStrike = sg2d.getFontInfo().fontStrike; + if (!(fontStrike instanceof CStrike)) return 0; + return ((CStrike)fontStrike).getNativeStrikePtr(); + } + + void drawGlyphVectorAsShape(final SunGraphics2D sg2d, final GlyphVector gv, final float x, final float y) { + final int length = gv.getNumGlyphs(); + for (int i = 0; i < length; i++) { + final Shape glyph = gv.getGlyphOutline(i, x, y); + sg2d.fill(glyph); + } + } + + void drawTextAsShape(final SunGraphics2D sg2d, final String s, final double x, final double y) { + final Object oldAliasingHint = sg2d.getRenderingHint(SunHints.KEY_ANTIALIASING); + final FontRenderContext frc = sg2d.getFontRenderContext(); + sg2d.setRenderingHint(SunHints.KEY_ANTIALIASING, (frc.isAntiAliased() ? SunHints.VALUE_ANTIALIAS_ON : SunHints.VALUE_ANTIALIAS_OFF)); + + final Font font = sg2d.getFont(); + final GlyphVector gv = font.createGlyphVector(frc, s); + final int length = gv.getNumGlyphs(); + for (int i = 0; i < length; i++) { + final Shape glyph = gv.getGlyphOutline(i, (float)x, (float)y); + sg2d.fill(glyph); + } + + sg2d.setRenderingHint(SunHints.KEY_ANTIALIASING, oldAliasingHint); + } + + public void drawString(final SunGraphics2D sg2d, final String s, final double x, final double y) { + final long nativeStrikePtr = getNativeStrikePtr(sg2d); + if (OSXSurfaceData.IsSimpleColor(sg2d.paint) && nativeStrikePtr != 0) { + final OSXSurfaceData surfaceData = (OSXSurfaceData)sg2d.getSurfaceData(); + surfaceData.drawString(this, sg2d, nativeStrikePtr, s, x, y); + } else { + drawTextAsShape(sg2d, s, x, y); + } + } + + public void drawGlyphVector(final SunGraphics2D sg2d, final GlyphVector gV, final float x, final float y) { + final Font prevFont = sg2d.getFont(); + sg2d.setFont(gV.getFont()); + + final long nativeStrikePtr = getNativeStrikePtr(sg2d); + if (OSXSurfaceData.IsSimpleColor(sg2d.paint) && nativeStrikePtr != 0) { + final OSXSurfaceData surfaceData = (OSXSurfaceData)sg2d.getSurfaceData(); + surfaceData.drawGlyphs(this, sg2d, nativeStrikePtr, gV, x, y); + } else { + drawGlyphVectorAsShape(sg2d, gV, x, y); + } + sg2d.setFont(prevFont); + } + + public void drawChars(final SunGraphics2D sg2d, final char data[], final int offset, final int length, final int x, final int y) { + final long nativeStrikePtr = getNativeStrikePtr(sg2d); + if (OSXSurfaceData.IsSimpleColor(sg2d.paint) && nativeStrikePtr != 0) { + final OSXSurfaceData surfaceData = (OSXSurfaceData)sg2d.getSurfaceData(); + surfaceData.drawUnicodes(this, sg2d, nativeStrikePtr, data, offset, length, x, y); + } else { + drawTextAsShape(sg2d, new String(data, offset, length), x, y); + } + } + + public CTextPipe traceWrap() { + return new Tracer(); + } + + public static class Tracer extends CTextPipe { + void doDrawString(final SurfaceData sData, final long nativeStrikePtr, final String s, final float x, final float y) { + GraphicsPrimitive.tracePrimitive("QuartzDrawString"); + super.doDrawString(sData, nativeStrikePtr, s, x, y); + } + + public void doDrawGlyphs(final SurfaceData sData, final long nativeStrikePtr, final GlyphVector gV, final float x, final float y) { + GraphicsPrimitive.tracePrimitive("QuartzDrawGlyphs"); + super.doDrawGlyphs(sData, nativeStrikePtr, gV, x, y); + } + + public void doUnicodes(final SurfaceData sData, final long nativeStrikePtr, final char unicodes[], final int offset, final int length, final float x, final float y) { + GraphicsPrimitive.tracePrimitive("QuartzDrawUnicodes"); + super.doUnicodes(sData, nativeStrikePtr, unicodes, offset, length, x, y); + } + + public void doOneUnicode(final SurfaceData sData, final long nativeStrikePtr, final char aUnicode, final float x, final float y) { + GraphicsPrimitive.tracePrimitive("QuartzDrawUnicode"); + super.doOneUnicode(sData, nativeStrikePtr, aUnicode, x, y); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/EventDispatchAccess.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + +// This exists strictly to work around the fact that java.awt.Conditional isn't a public class. +// It uses java reflection to get the EventDispatchThread class and call a MacOSX only +// method on it. +// +// NOTE: This uses reflection in its implementation, so it is not for performance critical code. +// +// See java.awt.EventDispatchThread and apple.awt.CPrintJob for more. +// +public abstract class EventDispatchAccess { + public native void pumpEventsAndWait(); + public abstract boolean evaluate(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/classes/sun/lwawt/macosx/NSPrintInfo.java Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2011, 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 sun.lwawt.macosx; + + +import java.io.*; +import javax.print.attribute.*; + +public final class NSPrintInfo implements PrintJobAttribute, PrintRequestAttribute, Serializable, Cloneable { + + private long fNSPrintInfo; + + public NSPrintInfo(long nsPrintInfo) { + fNSPrintInfo = nsPrintInfo; + } + + public long getValue() { + return fNSPrintInfo; + } + + public boolean equals(Object object) { + return (object != null && object instanceof NSPrintInfo && fNSPrintInfo == ((NSPrintInfo)object).fNSPrintInfo); + } + + public int hashCode() { + return (int)fNSPrintInfo; + } + + public String toString() { + return "" + fNSPrintInfo; + } + + public final Class<? extends Attribute> getCategory() { + return NSPrintInfo.class; + } + + public final String getName() { + return "nsPrintInfo"; + } +}
--- a/src/macosx/native/sun/awt/AWTWindow.m Fri Sep 23 21:13:13 2011 +0400 +++ b/src/macosx/native/sun/awt/AWTWindow.m Fri Sep 23 13:42:06 2011 -0700 @@ -328,9 +328,9 @@ } - (void) _deliverWindowFocusEvent:(BOOL)focused { -AWT_ASSERT_APPKIT_THREAD; +//AWT_ASSERT_APPKIT_THREAD; - JNIEnv *env = [ThreadUtilities getJNIEnv]; + JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env]; static JNF_MEMBER_CACHE(jm_deliverWindowFocusEvent, jc_CPlatformWindow, "deliverWindowFocusEvent", "(Z)V"); JNFCallVoidMethod(env, platformWindow, jm_deliverWindowFocusEvent, (jboolean)focused);
--- a/src/macosx/native/sun/awt/AWT_debug.h Fri Sep 23 21:13:13 2011 +0400 +++ b/src/macosx/native/sun/awt/AWT_debug.h Fri Sep 23 13:42:06 2011 -0700 @@ -32,7 +32,7 @@ #define kInternalError "java/lang/InternalError" #define AWT_DEBUG_LOG(str) \ - NSLog(@"Cocoa AWT: %@ (%s - %s:%d)", str, __FUNCTION__, __FILE__, __LINE__) + NSLog(@"Cocoa AWT: %@ %@", str, [NSThread callStackSymbols]) #define AWT_DEBUG_BUG_REPORT_MESSAGE \ NSLog(@"\tPlease file a bug report at http://java.net/jira/browse/MACOSX_PORT with this message and a reproducible test case.")
--- a/src/macosx/native/sun/awt/CDropTarget.m Fri Sep 23 21:13:13 2011 +0400 +++ b/src/macosx/native/sun/awt/CDropTarget.m Fri Sep 23 13:42:06 2011 -0700 @@ -77,7 +77,7 @@ if (control != nil) { - JNIEnv *env = [ThreadUtilities getJNIEnv]; + JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; fComponent = JNFNewGlobalRef(env, jcomponent); fDropTarget = JNFNewGlobalRef(env, jdropTarget);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/CPrinterJob.m Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2011, 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. + */ + + +#import "java_awt_print_PageFormat.h" +#import "java_awt_print_Pageable.h" +#import "sun_lwawt_macosx_CPrinterJob.h" +#import "sun_lwawt_macosx_CPrinterPageDialog.h" + +#import <Cocoa/Cocoa.h> +#import <JavaNativeFoundation/JavaNativeFoundation.h> + +#import "PrinterView.h" +#import "PrintModel.h" +#import "ThreadUtilities.h" +#import "GeomUtilities.h" + +static JNF_CLASS_CACHE(sjc_Paper, "java/awt/print/Paper"); +static JNF_CLASS_CACHE(sjc_PageFormat, "java/awt/print/PageFormat"); +static JNF_CLASS_CACHE(sjc_CPrinterJob, "sun/lwawt/macosx/CPrinterJob"); +static JNF_CLASS_CACHE(sjc_CPrinterDialog, "sun/lwawt/macosx/CPrinterDialog"); +static JNF_MEMBER_CACHE(sjm_getNSPrintInfo, sjc_CPrinterJob, "getNSPrintInfo", "()J"); +static JNF_MEMBER_CACHE(sjm_printerJob, sjc_CPrinterDialog, "fPrinterJob", "Lsun/lwawt/macosx/CPrinterJob;"); + +static NSPrintInfo* createDefaultNSPrintInfo(); + +static void makeBestFit(NSPrintInfo* src); + +static void nsPrintInfoToJavaPaper(JNIEnv* env, NSPrintInfo* src, jobject dst); +static void javaPaperToNSPrintInfo(JNIEnv* env, jobject src, NSPrintInfo* dst); + +static void nsPrintInfoToJavaPageFormat(JNIEnv* env, NSPrintInfo* src, jobject dst); +static void javaPageFormatToNSPrintInfo(JNIEnv* env, jobject srcPrinterJob, jobject srcPageFormat, NSPrintInfo* dst); + +static void nsPrintInfoToJavaPrinterJob(JNIEnv* env, NSPrintInfo* src, jobject dstPrinterJob, jobject dstPageable); +static void javaPrinterJobToNSPrintInfo(JNIEnv* env, jobject srcPrinterJob, jobject srcPageable, NSPrintInfo* dst); + + +static NSPrintInfo* createDefaultNSPrintInfo(JNIEnv* env, jstring printer) +{ + NSPrintInfo* defaultPrintInfo = [[NSPrintInfo sharedPrintInfo] copy]; + if (printer != NULL) + { + NSPrinter* nsPrinter = [NSPrinter printerWithName:JNFJavaToNSString(env, printer)]; + if (nsPrinter != nil) + { + [defaultPrintInfo setPrinter:nsPrinter]; + } + } + [defaultPrintInfo setUpPrintOperationDefaultValues]; + + // cmc 05/18/04 radr://3160443 : setUpPrintOperationDefaultValues sets the + // page margins to 72, 72, 90, 90 - need to use [NSPrintInfo imageablePageBounds] + // to get values from the printer. + // NOTE: currently [NSPrintInfo imageablePageBounds] does not update itself when + // the user selects a different printer - see radr://3657453. However, rather than + // directly querying the PPD here, we'll let AppKit printing do the work. The AppKit + // printing bug above is set to be fixed for Tiger. + NSRect imageableRect = [defaultPrintInfo imageablePageBounds]; + [defaultPrintInfo setLeftMargin: imageableRect.origin.x]; + [defaultPrintInfo setBottomMargin: imageableRect.origin.y]; //top and bottom are flipped because [NSPrintInfo imageablePageBounds] returns a flipped NSRect (bottom-left to top-right). + [defaultPrintInfo setRightMargin: [defaultPrintInfo paperSize].width-imageableRect.origin.x-imageableRect.size.width]; + [defaultPrintInfo setTopMargin: [defaultPrintInfo paperSize].height-imageableRect.origin.y-imageableRect.size.height]; + + return defaultPrintInfo; +} + +static void makeBestFit(NSPrintInfo* src) +{ + // This will look at the NSPrintInfo's margins. If they are out of bounds to the + // imageable area of the page, it will set them to the largest possible size. + + NSRect imageable = [src imageablePageBounds]; + + NSSize paperSize = [src paperSize]; + + CGFloat fullLeftM = imageable.origin.x; + CGFloat fullRightM = paperSize.width - (imageable.origin.x + imageable.size.width); + + // These are flipped because [NSPrintInfo imageablePageBounds] returns a flipped + // NSRect (bottom-left to top-right). + CGFloat fullTopM = paperSize.height - (imageable.origin.y + imageable.size.height); + CGFloat fullBottomM = imageable.origin.y; + + if (fullLeftM > [src leftMargin]) + { + [src setLeftMargin:fullLeftM]; + } + + if (fullRightM > [src rightMargin]) + { + [src setRightMargin:fullRightM]; + } + + if (fullTopM > [src topMargin]) + { + [src setTopMargin:fullTopM]; + } + + if (fullBottomM > [src bottomMargin]) + { + [src setBottomMargin:fullBottomM]; + } +} + +// In AppKit Printing, the rectangle is always oriented. In AppKit Printing, setting +// the rectangle will always set the orientation. +// In java printing, the rectangle is oriented if accessed from PageFormat. It is +// not oriented when accessed from Paper. + +static void nsPrintInfoToJavaPaper(JNIEnv* env, NSPrintInfo* src, jobject dst) +{ + static JNF_MEMBER_CACHE(jm_setSize, sjc_Paper, "setSize", "(DD)V"); + static JNF_MEMBER_CACHE(jm_setImageableArea, sjc_Paper, "setImageableArea", "(DDDD)V"); + + jdouble jPaperW, jPaperH; + + // NSPrintInfo paperSize is oriented. java Paper is not oriented. Take + // the -[NSPrintInfo orientation] into account when setting the Paper + // rectangle. + + NSSize paperSize = [src paperSize]; + switch ([src orientation]) { + case NSPortraitOrientation: + jPaperW = paperSize.width; + jPaperH = paperSize.height; + break; + + case NSLandscapeOrientation: + jPaperW = paperSize.height; + jPaperH = paperSize.width; + break; + + default: + jPaperW = paperSize.width; + jPaperH = paperSize.height; + break; + } + + JNFCallVoidMethod(env, dst, jm_setSize, jPaperW, jPaperH); // AWT_THREADING Safe (known object - always actual Paper) + + // Set the imageable area from the margins + CGFloat leftM = [src leftMargin]; + CGFloat rightM = [src rightMargin]; + CGFloat topM = [src topMargin]; + CGFloat bottomM = [src bottomMargin]; + + jdouble jImageX = leftM; + jdouble jImageY = topM; + jdouble jImageW = jPaperW - (leftM + rightM); + jdouble jImageH = jPaperH - (topM + bottomM); + + JNFCallVoidMethod(env, dst, jm_setImageableArea, jImageX, jImageY, jImageW, jImageH); // AWT_THREADING Safe (known object - always actual Paper) +} + +static void javaPaperToNSPrintInfo(JNIEnv* env, jobject src, NSPrintInfo* dst) +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_MEMBER_CACHE(jm_getWidth, sjc_Paper, "getWidth", "()D"); + static JNF_MEMBER_CACHE(jm_getHeight, sjc_Paper, "getHeight", "()D"); + static JNF_MEMBER_CACHE(jm_getImageableX, sjc_Paper, "getImageableX", "()D"); + static JNF_MEMBER_CACHE(jm_getImageableY, sjc_Paper, "getImageableY", "()D"); + static JNF_MEMBER_CACHE(jm_getImageableW, sjc_Paper, "getImageableWidth", "()D"); + static JNF_MEMBER_CACHE(jm_getImageableH, sjc_Paper, "getImageableHeight", "()D"); + + // java Paper is always Portrait oriented. Set NSPrintInfo with this + // rectangle, and it's orientation may change. If necessary, be sure to call + // -[NSPrintInfo setOrientation] after this call, which will then + // adjust the -[NSPrintInfo paperSize] as well. + + jdouble jPhysicalWidth = JNFCallDoubleMethod(env, src, jm_getWidth); // AWT_THREADING Safe (!appKit) + jdouble jPhysicalHeight = JNFCallDoubleMethod(env, src, jm_getHeight); // AWT_THREADING Safe (!appKit) + + [dst setPaperSize:NSMakeSize(jPhysicalWidth, jPhysicalHeight)]; + + // Set the margins from the imageable area + jdouble jImageX = JNFCallDoubleMethod(env, src, jm_getImageableX); // AWT_THREADING Safe (!appKit) + jdouble jImageY = JNFCallDoubleMethod(env, src, jm_getImageableY); // AWT_THREADING Safe (!appKit) + jdouble jImageW = JNFCallDoubleMethod(env, src, jm_getImageableW); // AWT_THREADING Safe (!appKit) + jdouble jImageH = JNFCallDoubleMethod(env, src, jm_getImageableH); // AWT_THREADING Safe (!appKit) + + [dst setLeftMargin:(CGFloat)jImageX]; + [dst setTopMargin:(CGFloat)jImageY]; + [dst setRightMargin:(CGFloat)(jPhysicalWidth - jImageW - jImageX)]; + [dst setBottomMargin:(CGFloat)(jPhysicalHeight - jImageH - jImageY)]; +} + +static void nsPrintInfoToJavaPageFormat(JNIEnv* env, NSPrintInfo* src, jobject dst) +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_MEMBER_CACHE(jm_setOrientation, sjc_PageFormat, "setOrientation", "(I)V"); + static JNF_MEMBER_CACHE(jm_setPaper, sjc_PageFormat, "setPaper", "(Ljava/awt/print/Paper;)V"); + static JNF_CTOR_CACHE(jm_Paper_ctor, sjc_Paper, "()V"); + + jint jOrientation; + NSPrintingOrientation nsOrientation = [src orientation]; + switch (nsOrientation) { + case NSPortraitOrientation: + jOrientation = java_awt_print_PageFormat_PORTRAIT; + break; + + case NSLandscapeOrientation: + jOrientation = java_awt_print_PageFormat_LANDSCAPE; //+++gdb Are LANDSCAPE and REVERSE_LANDSCAPE still inverted? + break; + +/* + // AppKit printing doesn't support REVERSE_LANDSCAPE. Radar 2960295. + case NSReverseLandscapeOrientation: + jOrientation = java_awt_print_PageFormat.REVERSE_LANDSCAPE; //+++gdb Are LANDSCAPE and REVERSE_LANDSCAPE still inverted? + break; +*/ + + default: + jOrientation = java_awt_print_PageFormat_PORTRAIT; + break; + } + + JNFCallVoidMethod(env, dst, jm_setOrientation, jOrientation); // AWT_THREADING Safe (!appKit) + + // Create a new Paper + jobject paper = JNFNewObject(env, jm_Paper_ctor); // AWT_THREADING Safe (known object) + + nsPrintInfoToJavaPaper(env, src, paper); + + // Set the Paper in the PageFormat + JNFCallVoidMethod(env, dst, jm_setPaper, paper); // AWT_THREADING Safe (!appKit) + + (*env)->DeleteLocalRef(env, paper); +} + +static void javaPageFormatToNSPrintInfo(JNIEnv* env, jobject srcPrintJob, jobject srcPageFormat, NSPrintInfo* dstPrintInfo) +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_MEMBER_CACHE(jm_getOrientation, sjc_PageFormat, "getOrientation", "()I"); + static JNF_MEMBER_CACHE(jm_getPaper, sjc_PageFormat, "getPaper", "()Ljava/awt/print/Paper;"); + static JNF_MEMBER_CACHE(jm_getPrinterName, sjc_CPrinterJob, "getPrinterName", "()Ljava/lang/String;"); + + // When setting page information (orientation, size) in NSPrintInfo, set the + // rectangle first. This is because setting the orientation will change the + // rectangle to match. + + // Set up the paper. This will force Portrait since java Paper is + // not oriented. Then setting the NSPrintInfo orientation below + // will flip NSPrintInfo's info as necessary. + jobject paper = JNFCallObjectMethod(env, srcPageFormat, jm_getPaper); // AWT_THREADING Safe (!appKit) + javaPaperToNSPrintInfo(env, paper, dstPrintInfo); + (*env)->DeleteLocalRef(env, paper); + + switch (JNFCallIntMethod(env, srcPageFormat, jm_getOrientation)) { // AWT_THREADING Safe (!appKit) + case java_awt_print_PageFormat_PORTRAIT: + [dstPrintInfo setOrientation:NSPortraitOrientation]; + break; + + case java_awt_print_PageFormat_LANDSCAPE: + [dstPrintInfo setOrientation:NSLandscapeOrientation]; //+++gdb Are LANDSCAPE and REVERSE_LANDSCAPE still inverted? + break; + + // AppKit printing doesn't support REVERSE_LANDSCAPE. Radar 2960295. + case java_awt_print_PageFormat_REVERSE_LANDSCAPE: + [dstPrintInfo setOrientation:NSLandscapeOrientation]; //+++gdb Are LANDSCAPE and REVERSE_LANDSCAPE still inverted? + break; + + default: + [dstPrintInfo setOrientation:NSPortraitOrientation]; + break; + } + + // <rdar://problem/4022422> NSPrinterInfo is not correctly set to the selected printer + // from the Java side of CPrinterJob. Has always assumed the default printer was the one we wanted. + if (srcPrintJob == NULL) return; + jobject printerNameObj = JNFCallObjectMethod(env, srcPrintJob, jm_getPrinterName); + if (printerNameObj == NULL) return; + NSString *printerName = JNFJavaToNSString(env, printerNameObj); + if (printerName == nil) return; + NSPrinter *printer = [NSPrinter printerWithName:printerName]; + if (printer == nil) return; + [dstPrintInfo setPrinter:printer]; +} + +static void nsPrintInfoToJavaPrinterJob(JNIEnv* env, NSPrintInfo* src, jobject dstPrinterJob, jobject dstPageable) +{ + static JNF_MEMBER_CACHE(jm_setService, sjc_CPrinterJob, "setPrinterServiceFromNative", "(Ljava/lang/String;)V"); + static JNF_MEMBER_CACHE(jm_setCopies, sjc_CPrinterJob, "setCopies", "(I)V"); + static JNF_MEMBER_CACHE(jm_setCollated, sjc_CPrinterJob, "setCollated", "(Z)V"); + static JNF_MEMBER_CACHE(jm_setPageRange, sjc_CPrinterJob, "setPageRange", "(II)V"); + + // get the selected printer's name, and set the appropriate PrintService on the Java side + NSString *name = [[src printer] name]; + jstring printerName = JNFNSToJavaString(env, name); + JNFCallVoidMethod(env, dstPrinterJob, jm_setService, printerName); + + + NSMutableDictionary* printingDictionary = [src dictionary]; + + NSNumber* nsCopies = [printingDictionary objectForKey:NSPrintCopies]; + if ([nsCopies respondsToSelector:@selector(integerValue)]) + { + JNFCallVoidMethod(env, dstPrinterJob, jm_setCopies, [nsCopies integerValue]); // AWT_THREADING Safe (known object) + } + + NSNumber* nsCollated = [printingDictionary objectForKey:NSPrintMustCollate]; + if ([nsCollated respondsToSelector:@selector(boolValue)]) + { + JNFCallVoidMethod(env, dstPrinterJob, jm_setCollated, [nsCollated boolValue] ? JNI_TRUE : JNI_FALSE); // AWT_THREADING Safe (known object) + } + + NSNumber* nsPrintAllPages = [printingDictionary objectForKey:NSPrintAllPages]; + if ([nsPrintAllPages respondsToSelector:@selector(boolValue)]) + { + jint jFirstPage = 0, jLastPage = java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES; + if (![nsPrintAllPages boolValue]) + { + NSNumber* nsFirstPage = [printingDictionary objectForKey:NSPrintFirstPage]; + if ([nsFirstPage respondsToSelector:@selector(integerValue)]) + { + jFirstPage = [nsFirstPage integerValue] - 1; + } + + NSNumber* nsLastPage = [printingDictionary objectForKey:NSPrintLastPage]; + if ([nsLastPage respondsToSelector:@selector(integerValue)]) + { + jLastPage = [nsLastPage integerValue] - 1; + } + } + + JNFCallVoidMethod(env, dstPrinterJob, jm_setPageRange, jFirstPage, jLastPage); // AWT_THREADING Safe (known object) + } +} + +static void javaPrinterJobToNSPrintInfo(JNIEnv* env, jobject srcPrinterJob, jobject srcPageable, NSPrintInfo* dst) +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_CLASS_CACHE(jc_Pageable, "java/awt/print/Pageable"); + static JNF_MEMBER_CACHE(jm_getCopies, sjc_CPrinterJob, "getCopiesInt", "()I"); + static JNF_MEMBER_CACHE(jm_isCollated, sjc_CPrinterJob, "isCollated", "()Z"); + static JNF_MEMBER_CACHE(jm_getNumberOfPages, jc_Pageable, "getNumberOfPages", "()I"); + + NSMutableDictionary* printingDictionary = [dst dictionary]; + + jint copies = JNFCallIntMethod(env, srcPrinterJob, jm_getCopies); // AWT_THREADING Safe (known object) + [printingDictionary setObject:[NSNumber numberWithInteger:copies] forKey:NSPrintCopies]; + + jboolean collated = JNFCallBooleanMethod(env, srcPrinterJob, jm_isCollated); // AWT_THREADING Safe (known object) + [printingDictionary setObject:[NSNumber numberWithBool:collated ? YES : NO] forKey:NSPrintMustCollate]; + + jint jNumPages = JNFCallIntMethod(env, srcPageable, jm_getNumberOfPages); // AWT_THREADING Safe (!appKit) + if (jNumPages != java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES) + { + [printingDictionary setObject:[NSNumber numberWithBool:NO] forKey:NSPrintAllPages]; + + [printingDictionary setObject:[NSNumber numberWithInteger:1] forKey:NSPrintFirstPage]; + [printingDictionary setObject:[NSNumber numberWithInteger:jNumPages] forKey:NSPrintLastPage]; + } + else + { + [printingDictionary setObject:[NSNumber numberWithBool:YES] forKey:NSPrintAllPages]; + } +} + +/* + * Class: sun_lwawt_macosx_EventDispatchAccess + * Method: pumpEventsAndWait + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_EventDispatchAccess_pumpEventsAndWait +(JNIEnv *env, jobject eda) +{ + static JNF_CLASS_CACHE(jc_Thread, "java/lang/Thread"); + static JNF_STATIC_MEMBER_CACHE(jm_currentThread, jc_Thread, "currentThread", "()Ljava/lang/Thread;"); + static JNF_CLASS_CACHE(jc_EventDispatchThread, "java/awt/EventDispatchThread"); + static JNF_MEMBER_CACHE(jm_macosxGetConditional, jc_EventDispatchThread, "_macosxGetConditional", "(Ljava/lang/Object;)Ljava/awt/Conditional;"); + static JNF_MEMBER_CACHE(jm_pumpEvents, jc_EventDispatchThread, "pumpEvents", "(Ljava/awt/Conditional;)V"); + +JNF_COCOA_DURING(env); + + jobject thread = JNFCallStaticObjectMethod(env, jm_currentThread); + jobject conditional = JNFCallObjectMethod(env, thread, jm_macosxGetConditional, eda); + if (conditional != NULL) { + JNFCallVoidMethod(env, thread, jm_pumpEvents, conditional); + } + +JNF_COCOA_HANDLE(env); +} + +/* + * Class: sun_lwawt_macosx_CPrinterJob + * Method: abortDoc + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPrinterJob_abortDoc + (JNIEnv *env, jobject jthis) +{ +JNF_COCOA_ENTER(env); + // This is only called during the printLoop from the printLoop thread + NSPrintOperation* printLoop = [NSPrintOperation currentOperation]; + NSPrintInfo* printInfo = [printLoop printInfo]; + [printInfo setJobDisposition:NSPrintCancelJob]; +JNF_COCOA_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CPrinterJob + * Method: getDefaultPage + * Signature: (Ljava/awt/print/PageFormat;)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPrinterJob_getDefaultPage + (JNIEnv *env, jobject jthis, jobject page) +{ +JNF_COCOA_ENTER(env); + NSPrintInfo* printInfo = createDefaultNSPrintInfo(env, NULL); + + nsPrintInfoToJavaPageFormat(env, printInfo, page); + + [printInfo release]; +JNF_COCOA_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CPrinterJob + * Method: validatePaper + * Signature: (Ljava/awt/print/Paper;Ljava/awt/print/Paper;)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPrinterJob_validatePaper + (JNIEnv *env, jobject jthis, jobject origpaper, jobject newpaper) +{ +JNF_COCOA_ENTER(env); + + NSPrintInfo* printInfo = createDefaultNSPrintInfo(env, NULL); + javaPaperToNSPrintInfo(env, origpaper, printInfo); + makeBestFit(printInfo); + nsPrintInfoToJavaPaper(env, printInfo, newpaper); + [printInfo release]; + +JNF_COCOA_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CPrinterJob + * Method: createNSPrintInfo + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CPrinterJob_createNSPrintInfo + (JNIEnv *env, jobject jthis) +{ + jlong result = -1; +JNF_COCOA_ENTER(env); + // This is used to create the NSPrintInfo for this PrinterJob. Thread + // safety is assured by the java side of this call. + + NSPrintInfo* printInfo = createDefaultNSPrintInfo(env, NULL); + if (printInfo) CFRetain(printInfo); // GC + [printInfo release]; + + result = ptr_to_jlong(printInfo); + +JNF_COCOA_EXIT(env); + return result; +} + +/* + * Class: sun_lwawt_macosx_CPrinterJob + * Method: dispose + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPrinterJob_dispose + (JNIEnv *env, jobject jthis, jlong nsPrintInfo) +{ +JNF_COCOA_ENTER(env); + if (nsPrintInfo != -1) + { + NSPrintInfo* printInfo = (NSPrintInfo*)jlong_to_ptr(nsPrintInfo); + if (printInfo) CFRelease(printInfo); // GC + } +JNF_COCOA_EXIT(env); +} + + +/* + * Class: sun_lwawt_macosx_CPrinterJob + * Method: printLoop + * Signature: ()V + */ +JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPrinterJob_printLoop + (JNIEnv *env, jobject jthis, jboolean blocks, jint firstPage, jint lastPage) +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_MEMBER_CACHE(jm_getPageFormat, sjc_CPrinterJob, "getPageFormat", "(I)Ljava/awt/print/PageFormat;"); + static JNF_MEMBER_CACHE(jm_getPageFormatArea, sjc_CPrinterJob, "getPageFormatArea", "(Ljava/awt/print/PageFormat;)Ljava/awt/geom/Rectangle2D;"); + static JNF_MEMBER_CACHE(jm_getPrinterName, sjc_CPrinterJob, "getPrinterName", "()Ljava/lang/String;"); + static JNF_MEMBER_CACHE(jm_getPageable, sjc_CPrinterJob, "getPageable", "()Ljava/awt/print/Pageable;"); + + jboolean retVal = JNI_FALSE; + +JNF_COCOA_ENTER(env); + // Get the first page's PageFormat for setting things up (This introduces + // and is a facet of the same problem in Radar 2818593/2708932). + jobject page = JNFCallObjectMethod(env, jthis, jm_getPageFormat, 0); // AWT_THREADING Safe (!appKit) + if (page != NULL) { + jobject pageFormatArea = JNFCallObjectMethod(env, jthis, jm_getPageFormatArea, page); // AWT_THREADING Safe (!appKit) + + PrinterView* printerView = [[PrinterView alloc] initWithFrame:JavaToNSRect(env, pageFormatArea) withEnv:env withPrinterJob:jthis]; + [printerView setFirstPage:firstPage lastPage:lastPage]; + + NSPrintInfo* printInfo = (NSPrintInfo*)jlong_to_ptr(JNFCallLongMethod(env, jthis, sjm_getNSPrintInfo)); // AWT_THREADING Safe (known object) + + // <rdar://problem/4156975> passing jthis CPrinterJob as well, so we can extract the printer name from the current job + javaPageFormatToNSPrintInfo(env, jthis, page, printInfo); + + // <rdar://problem/4093799> NSPrinterInfo is not correctly set to the selected printer + // from the Java side of CPrinterJob. Had always assumed the default printer was the one we wanted. + jobject printerNameObj = JNFCallObjectMethod(env, jthis, jm_getPrinterName); + if (printerNameObj != NULL) { + NSString *printerName = JNFJavaToNSString(env, printerNameObj); + if (printerName != nil) { + NSPrinter *printer = [NSPrinter printerWithName:printerName]; + if (printer != nil) [printInfo setPrinter:printer]; + } + } + + // <rdar://problem/4367998> JTable.print attributes are ignored + jobject pageable = JNFCallObjectMethod(env, jthis, jm_getPageable); // AWT_THREADING Safe (!appKit) + javaPrinterJobToNSPrintInfo(env, jthis, pageable, printInfo); + + PrintModel* printModel = [[PrintModel alloc] initWithPrintInfo:printInfo]; + + (void)[printModel runPrintLoopWithView:printerView waitUntilDone:blocks withEnv:env]; + + // Only set this if we got far enough to call runPrintLoopWithView, or we will spin CPrinterJob.print() forever! + retVal = JNI_TRUE; + + [printModel release]; + [printerView release]; + + if (page != NULL) + { + (*env)->DeleteLocalRef(env, page); + } + + if (pageFormatArea != NULL) + { + (*env)->DeleteLocalRef(env, pageFormatArea); + } + } +JNF_COCOA_EXIT(env); + return retVal; +} + +/* + * Class: sun_lwawt_macosx_CPrinterPageDialog + * Method: showDialog + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPrinterPageDialog_showDialog + (JNIEnv *env, jobject jthis) +{ + + static JNF_CLASS_CACHE(jc_CPrinterPageDialog, "sun/lwawt/macosx/CPrinterPageDialog"); + static JNF_MEMBER_CACHE(jm_page, jc_CPrinterPageDialog, "fPage", "Ljava/awt/print/PageFormat;"); + + jboolean result = JNI_FALSE; +JNF_COCOA_ENTER(env); + jobject printerJob = JNFGetObjectField(env, jthis, sjm_printerJob); + NSPrintInfo* printInfo = (NSPrintInfo*)jlong_to_ptr(JNFCallLongMethod(env, printerJob, sjm_getNSPrintInfo)); // AWT_THREADING Safe (known object) + + jobject page = JNFGetObjectField(env, jthis, jm_page); + + // <rdar://problem/4156975> passing NULL, because only a CPrinterJob has a real printer associated with it + javaPageFormatToNSPrintInfo(env, NULL, page, printInfo); + + PrintModel* printModel = [[PrintModel alloc] initWithPrintInfo:printInfo]; + result = [printModel runPageSetup]; + [printModel release]; + + if (result) + { + nsPrintInfoToJavaPageFormat(env, printInfo, page); + } + + if (printerJob != NULL) + { + (*env)->DeleteLocalRef(env, printerJob); + } + + if (page != NULL) + { + (*env)->DeleteLocalRef(env, page); + } + +JNF_COCOA_EXIT(env); + return result; +} + +/* + * Class: sun_lwawt_macosx_CPrinterJobDialog + * Method: showDialog + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPrinterJobDialog_showDialog + (JNIEnv *env, jobject jthis) +{ + static JNF_CLASS_CACHE(jc_CPrinterJobDialog, "sun/lwawt/macosx/CPrinterJobDialog"); + static JNF_MEMBER_CACHE(jm_pageable, jc_CPrinterJobDialog, "fPageable", "Ljava/awt/print/Pageable;"); + + jboolean result = JNI_FALSE; +JNF_COCOA_ENTER(env); + jobject printerJob = JNFGetObjectField(env, jthis, sjm_printerJob); + NSPrintInfo* printInfo = (NSPrintInfo*)jlong_to_ptr(JNFCallLongMethod(env, printerJob, sjm_getNSPrintInfo)); // AWT_THREADING Safe (known object) + + jobject pageable = JNFGetObjectField(env, jthis, jm_pageable); + + javaPrinterJobToNSPrintInfo(env, printerJob, pageable, printInfo); + + PrintModel* printModel = [[PrintModel alloc] initWithPrintInfo:printInfo]; + result = [printModel runJobSetup]; + [printModel release]; + + if (result) + { + nsPrintInfoToJavaPrinterJob(env, printInfo, printerJob, pageable); + } + + if (printerJob != NULL) + { + (*env)->DeleteLocalRef(env, printerJob); + } + + if (pageable != NULL) + { + (*env)->DeleteLocalRef(env, pageable); + } + +JNF_COCOA_EXIT(env); + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/CTextPipe.m Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,653 @@ +/* + * Copyright (c) 2011, 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. + */ + +// Native side of the Quartz text pipe, paints on Quartz Surface Datas. +// Interesting Docs : /Developer/Documentation/Cocoa/TasksAndConcepts/ProgrammingTopics/FontHandling/FontHandling.html + +#import "sun_awt_SunHints.h" +#import "sun_lwawt_macosx_CTextPipe.h" +#import "sun_java2d_OSXSurfaceData.h" + +#import <JavaNativeFoundation/JavaNativeFoundation.h> + +#import "CoreTextSupport.h" +#import "QuartzSurfaceData.h" +#include "AWTStrike.h" + + +static const CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 }; + + +#pragma mark --- CoreText Support --- + + +// Translates a Unicode into a CGGlyph/CTFontRef pair +// Returns the substituted font, and places the appropriate glyph into "glyphRef" +CTFontRef JavaCT_CopyCTFallbackFontAndGlyphForUnicode +(const AWTFont *font, const UTF16Char *charRef, CGGlyph *glyphRef, int count) { + CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font->fFont, charRef, count); + if (fallback == NULL) + { + // use the original font if we somehow got duped into trying to fallback something we can't + fallback = (CTFontRef)font->fFont; + CFRetain(fallback); + } + + CTFontGetGlyphsForCharacters(fallback, charRef, glyphRef, count); + return fallback; +} + +// Translates a Java glyph code int (might be a negative unicode value) into a CGGlyph/CTFontRef pair +// Returns the substituted font, and places the appropriate glyph into "glyph" +CTFontRef JavaCT_CopyCTFallbackFontAndGlyphForJavaGlyphCode +(const AWTFont *font, const jint glyphCode, CGGlyph *glyphRef) +{ + // negative glyph codes are really unicodes, which were placed there by the mapper + // to indicate we should use CoreText to substitute the character + if (glyphCode >= 0) + { + *glyphRef = glyphCode; + CFRetain(font->fFont); + return (CTFontRef)font->fFont; + } + + UTF16Char character = -glyphCode; + return JavaCT_CopyCTFallbackFontAndGlyphForUnicode(font, &character, glyphRef, 1); +} + +// Breakup a 32 bit unicode value into the component surrogate pairs +void JavaCT_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]) { + int value = uniChar - 0x10000; + UTF16Char low_surrogate = (value & 0x3FF) | LO_SURROGATE_START; + UTF16Char high_surrogate = (((int)(value & 0xFFC00)) >> 10) | HI_SURROGATE_START; + charRef[0] = high_surrogate; + charRef[1] = low_surrogate; +} + + + +/* + * Callback for CoreText which uses the CoreTextProviderStruct to feed CT UniChars + * We only use it for one-off lines, and don't attempt to fragment our strings + */ +const UniChar *Java_CTProvider +(CFIndex stringIndex, CFIndex *charCount, CFDictionaryRef *attributes, void *refCon) +{ + // if we have a zero length string we can just return NULL for the string + // or if the index anything other than 0 we are not using core text + // correctly since we only have one run. + if (stringIndex != 0) + { + return NULL; + } + + CTS_ProviderStruct *ctps = (CTS_ProviderStruct *)refCon; + *charCount = ctps->length; + *attributes = ctps->attributes; + return ctps->unicodes; +} + + +/* + * Gets a Dictionary filled with common details we want to use for CoreText when we are interacting + * with it from Java. + */ +static NSDictionary* ctsDictionaryFor(const NSFont *font, BOOL useFractionalMetrics) +{ + NSNumber *gZeroNumber = [NSNumber numberWithInt:0]; + NSNumber *gOneNumber = [NSNumber numberWithInt:1]; + + return [NSDictionary dictionaryWithObjectsAndKeys: + font, NSFontAttributeName, + gOneNumber, (id)kCTForegroundColorFromContextAttributeName, + useFractionalMetrics ? gZeroNumber : gOneNumber, @"CTIntegerMetrics", // force integer hack in CoreText to help with Java's integer assumptions + gZeroNumber, NSLigatureAttributeName, + gZeroNumber, NSKernAttributeName, + nil]; +} + +// Itterates though each glyph, and if a transform is present for that glyph, apply it to the CGContext, and strike the glyph. +// If there is no per-glyph transform, just strike the glyph. Advances must also be transformed on-the-spot as well. +void JavaCT_DrawGlyphVector +(const QuartzSDOps *qsdo, const AWTStrike *strike, const BOOL useSubstituion, const int uniChars[], const CGGlyph glyphs[], CGSize advances[], const jint g_gvTXIndicesAsInts[], const jdouble g_gvTransformsAsDoubles[], const CFIndex length) +{ + CGPoint pt = { 0, 0 }; + + // get our baseline transform and font + CGContextRef cgRef = qsdo->cgRef; + CGAffineTransform ctmText = CGContextGetTextMatrix(cgRef); + //CGFontRef cgFont = CGContextGetFont(cgRef); + + CGContextSaveGState(cgRef); + CGAffineTransform invTx = CGAffineTransformInvert(strike->fTx); + + NSUInteger i; + for (i = 0; i < length; i++) + { + CGGlyph glyph = glyphs[i]; + int uniChar = uniChars[i]; + // if we found a unichar instead of a glyph code, get the fallback font, + // find the glyph code for the fallback font, and set the font on the current context + if (uniChar != 0) + { + CTFontRef fallback; + if (uniChar > 0xFFFF) { + UTF16Char charRef[2]; + JavaCT_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef); + CGGlyph glyphTmp[2]; + fallback = JavaCT_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2); + glyph = glyphTmp[0]; + } else { + const UTF16Char u = uniChar; + fallback = JavaCT_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, &u, (CGGlyph *)&glyph, 1); + } + if (fallback) { + const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL); + CFRelease(fallback); + + if (cgFallback) { + CGContextSetFont(cgRef, cgFallback); + CFRelease(cgFallback); + } + } + } + + // if we have per-glyph transformations + int tin = (g_gvTXIndicesAsInts == NULL) ? -1 : (g_gvTXIndicesAsInts[i] - 1) * 6; + if (tin < 0) + { + CGContextShowGlyphsAtPoint(cgRef, pt.x, pt.y, &glyph, 1); + } + else + { + CGAffineTransform tx = CGAffineTransformMake( + (CGFloat)g_gvTransformsAsDoubles[tin + 0], (CGFloat)g_gvTransformsAsDoubles[tin + 2], + (CGFloat)g_gvTransformsAsDoubles[tin + 1], (CGFloat)g_gvTransformsAsDoubles[tin + 3], + 0, 0); + + CGPoint txOffset = { (CGFloat)g_gvTransformsAsDoubles[tin + 4], (CGFloat)g_gvTransformsAsDoubles[tin + 5] }; + + txOffset = CGPointApplyAffineTransform(txOffset, invTx); + + // apply the transform, strike the glyph, can change the transform back + CGContextSetTextMatrix(cgRef, CGAffineTransformConcat(ctmText, tx)); + CGContextShowGlyphsAtPoint(cgRef, txOffset.x + pt.x, txOffset.y + pt.y, &glyph, 1); + CGContextSetTextMatrix(cgRef, ctmText); + + // transform the measured advance for this strike + advances[i] = CGSizeApplyAffineTransform(advances[i], tx); + advances[i].width += txOffset.x; + advances[i].height += txOffset.y; + } + + // move our next x,y + pt.x += advances[i].width; + pt.y += advances[i].height; + + // reset the font on the context after striking a unicode with CoreText + if (uniChar != 0) + { + // CGContextSetFont(cgRef, cgFont); + CGContextSaveGState(cgRef); + } + } +} + +// Using the Quartz Surface Data context, draw a hot-substituted character run +void JavaCT_DrawTextUsingQSD(JNIEnv *env, const QuartzSDOps *qsdo, const AWTStrike *strike, const jchar *chars, const jsize length) +{ + CGContextRef cgRef = qsdo->cgRef; + + AWTFont *awtFont = strike->fAWTFont; + CGFloat ptSize = strike->fSize; + CGAffineTransform tx = strike->fFontTx; + + NSFont *nsFont = [NSFont fontWithName:[awtFont->fFont fontName] size:ptSize]; + + if (ptSize != 0) { + CGFloat invScale = 1 / ptSize; + tx = CGAffineTransformConcat(tx, CGAffineTransformMakeScale(invScale, invScale)); + CGContextConcatCTM(cgRef, tx); + } + + CGContextSetTextMatrix(cgRef, CGAffineTransformIdentity); // resets the damage from CoreText + + NSString *string = [NSString stringWithCharacters:chars length:length]; + NSAttributedString *attribString = [[NSAttributedString alloc] initWithString:string]; + + CTTypesetterRef typeSetterRef = CTTypesetterCreateWithAttributedStringAndOptions((CFAttributedStringRef) attribString, (CFDictionaryRef) ctsDictionaryFor(nsFont, JRSFontStyleUsesFractionalMetrics(strike->fStyle))); + + CFRange range = {0, length}; + CTLineRef lineRef = CTTypesetterCreateLine(typeSetterRef, range); + + CTLineDraw(lineRef, cgRef); + + [attribString release]; + CFRelease(lineRef); + CFRelease(typeSetterRef); +} + + +/*---------------------- + DrawTextContext is the funnel for all of our CoreText drawing. + All three JNI apis call through this method. + ----------------------*/ +static void DrawTextContext +(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, const jchar *chars, const jsize length, const jdouble x, const jdouble y) +{ + if (length == 0) + { + return; + } + + qsdo->BeginSurface(env, qsdo, SD_Text); + if (qsdo->cgRef == NULL) + { + qsdo->FinishSurface(env, qsdo); + return; + } + + CGContextRef cgRef = qsdo->cgRef; + + + CGContextSaveGState(cgRef); + JRSFontSetRenderingStyleOnContext(cgRef, strike->fStyle); + + // we want to translate before we transform (scale or rotate) <rdar://4042541> (vm) + CGContextTranslateCTM(cgRef, x, y); + + AWTFont *awtfont = strike->fAWTFont; //(AWTFont *)(qsdo->fontInfo.awtfont); + NSCharacterSet *charSet = [awtfont->fFont coveredCharacterSet]; + + JavaCT_DrawTextUsingQSD(env, qsdo, strike, chars, length); // Draw with CoreText + + CGContextRestoreGState(cgRef); + + qsdo->FinishSurface(env, qsdo); +} + +#pragma mark --- Glyph Vector Pipeline --- + +/*----------------------------------- + Glyph Vector Pipeline + + doDrawGlyphs() has been separated into several pipelined functions to increase performance, + and improve accountability for JNI resources, malloc'd memory, and error handling. + + Each stage of the pipeline is responsible for doing only one major thing, like allocating buffers, + aquiring transform arrays from JNI, filling buffers, or striking glyphs. All resources or memory + aquired at a given stage, must be released in that stage. Any error that occurs (like a failed malloc) + is to be handled in the stage it occurs in, and is to return immediatly after freeing it's resources. + +-----------------------------------*/ + +static JNF_CLASS_CACHE(jc_StandardGlyphVector, "sun/font/StandardGlyphVector"); + +// Checks the GlyphVector Java object for any transforms that were applied to individual characters. If none are present, +// strike the glyphs immediately in Core Graphics. Otherwise, obtain the arrays, and defer to above. +static inline void doDrawGlyphsPipe_checkForPerGlyphTransforms +(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, BOOL useSubstituion, int *uniChars, CGGlyph *glyphs, CGSize *advances, size_t length) +{ + // if we have no character substitution, and no per-glyph transformations - strike now! + static JNF_MEMBER_CACHE(jm_StandardGlyphVector_gti, jc_StandardGlyphVector, "gti", "Lsun/font/StandardGlyphVector$GlyphTransformInfo;"); + jobject gti = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_gti); + if (gti == 0) + { + if (useSubstituion) + { + // quasi-simple case, substitution, but no per-glyph transforms + JavaCT_DrawGlyphVector(qsdo, strike, TRUE, uniChars, glyphs, advances, NULL, NULL, length); + } + else + { + // fast path, straight to CG without per-glyph transforms + CGContextShowGlyphsWithAdvances(qsdo->cgRef, glyphs, advances, length); + } + return; + } + + static JNF_CLASS_CACHE(jc_StandardGlyphVector_GlyphTransformInfo, "sun/font/StandardGlyphVector$GlyphTransformInfo"); + static JNF_MEMBER_CACHE(jm_StandardGlyphVector_GlyphTransformInfo_transforms, jc_StandardGlyphVector_GlyphTransformInfo, "transforms", "[D"); + jdoubleArray g_gtiTransformsArray = JNFGetObjectField(env, gti, jm_StandardGlyphVector_GlyphTransformInfo_transforms); //(*env)->GetObjectField(env, gti, g_gtiTransforms); + jdouble *g_gvTransformsAsDoubles = (*env)->GetPrimitiveArrayCritical(env, g_gtiTransformsArray, NULL); + + static JNF_MEMBER_CACHE(jm_StandardGlyphVector_GlyphTransformInfo_indices, jc_StandardGlyphVector_GlyphTransformInfo, "indices", "[I"); + jintArray g_gtiTXIndicesArray = JNFGetObjectField(env, gti, jm_StandardGlyphVector_GlyphTransformInfo_indices); + jint *g_gvTXIndicesAsInts = (*env)->GetPrimitiveArrayCritical(env, g_gtiTXIndicesArray, NULL); + + // slowest case, we have per-glyph transforms, and possibly glyph substitution as well + JavaCT_DrawGlyphVector(qsdo, strike, useSubstituion, uniChars, glyphs, advances, g_gvTXIndicesAsInts, g_gvTransformsAsDoubles, length); + + (*env)->ReleasePrimitiveArrayCritical(env, g_gtiTransformsArray, g_gvTransformsAsDoubles, JNI_ABORT); + (*env)->DeleteLocalRef(env, g_gtiTransformsArray); + + (*env)->ReleasePrimitiveArrayCritical(env, g_gtiTXIndicesArray, g_gvTXIndicesAsInts, JNI_ABORT); + (*env)->DeleteLocalRef(env, g_gtiTXIndicesArray); +} + +// Retrieves advances for translated unicodes +// Uses "glyphs" as a temporary buffer for the glyph-to-unicode translation +void JavaCT_GetAdvancesForUnichars +(const NSFont *font, const int uniChars[], CGGlyph glyphs[], const size_t length, CGSize advances[]) +{ + // cycle over each spot, and if we discovered a unicode to substitute, we have to calculate the advance for it + size_t i; + for (i = 0; i < length; i++) + { + UniChar uniChar = uniChars[i]; + if (uniChar == 0) continue; + + CGGlyph glyph = 0; + const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font, &uniChar, 1); + if (fallback) { + CTFontGetGlyphsForCharacters(fallback, &uniChar, &glyph, 1); + CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &(advances[i]), 1); + CFRelease(fallback); + } + + glyphs[i] = glyph; + } +} + +// Fills the glyph buffer with glyphs from the GlyphVector object. Also checks to see if the glyph's positions have been +// already caculated from GlyphVector, or we simply ask Core Graphics to make some advances for us. Pre-calculated positions +// are translated into advances, since CG only understands advances. +static inline void doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers +(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, CGGlyph *glyphs, int *uniChars, CGSize *advances, size_t length, jintArray glyphsArray) +{ + // fill the glyph buffer + jint *glyphsAsInts = (*env)->GetPrimitiveArrayCritical(env, glyphsArray, NULL); + + // if a glyph code from Java is negative, that means it is really a unicode value + // which we can use in CoreText to strike the character in another font + size_t i; + BOOL complex = NO; + for (i = 0; i < length; i++) + { + jint code = glyphsAsInts[i]; + if (code < 0) + { + complex = YES; + uniChars[i] = -code; + glyphs[i] = 0; + } + else + { + uniChars[i] = 0; + glyphs[i] = code; + } + } + + (*env)->ReleasePrimitiveArrayCritical(env, glyphsArray, glyphsAsInts, JNI_ABORT); + + // fill the advance buffer + static JNF_MEMBER_CACHE(jm_StandardGlyphVector_positions, jc_StandardGlyphVector, "positions", "[F"); + jfloatArray posArray = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_positions); + if (posArray != NULL) + { + // in this case, the positions have already been pre-calculated for us on the Java side + + jfloat *positions = (*env)->GetPrimitiveArrayCritical(env, posArray, NULL); + CGPoint prev; + prev.x = positions[0]; + prev.y = positions[1]; + + // <rdar://problem/4294061> take the first point, and move the context to that location + CGContextTranslateCTM(qsdo->cgRef, prev.x, prev.y); + + CGAffineTransform invTx = CGAffineTransformInvert(strike->fFontTx); + + // for each position, figure out the advance (since CG won't take positions directly) + size_t i; + for (i = 0; i < length - 1; i++) + { + size_t i2 = (i+1) * 2; + CGPoint pt; + pt.x = positions[i2]; + pt.y = positions[i2+1]; + pt = CGPointApplyAffineTransform(pt, invTx); + advances[i].width = pt.x - prev.x; + advances[i].height = -(pt.y - prev.y); // negative to translate to device space + prev.x = pt.x; + prev.y = pt.y; + } + + (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, JNI_ABORT); + (*env)->DeleteLocalRef(env, posArray); + } + else + { + // in this case, we have to go and calculate the positions ourselves + // there were no pre-calculated positions from the glyph buffer on the Java side + AWTFont *awtFont = strike->fAWTFont; + CTFontGetAdvancesForGlyphs((CTFontRef)awtFont->fFont, kCTFontDefaultOrientation, glyphs, advances, length); + + if (complex) + { + JavaCT_GetAdvancesForUnichars(awtFont->fFont, uniChars, glyphs, length, advances); + } + } + + // continue on to the next stage of the pipe + doDrawGlyphsPipe_checkForPerGlyphTransforms(env, qsdo, strike, gVector, complex, uniChars, glyphs, advances, length); +} + +// Obtains the glyph array to determine the number of glyphs we are dealing with. If we are dealing a large number of glyphs, +// we malloc a buffer to hold the glyphs and their advances, otherwise we use stack allocated buffers. +static inline void doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc +(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector) +{ + static JNF_MEMBER_CACHE(jm_StandardGlyphVector_glyphs, jc_StandardGlyphVector, "glyphs", "[I"); + jintArray glyphsArray = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_glyphs); + jsize length = (*env)->GetArrayLength(env, glyphsArray); + + if (length == 0) + { + // nothing to draw + (*env)->DeleteLocalRef(env, glyphsArray); + return; + } + + if (length < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) + { + // if we are small enough, fit everything onto the stack + CGGlyph glyphs[length]; + int uniChars[length]; + CGSize advances[length]; + doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray); + } + else + { + // otherwise, we should malloc and free buffers for this large run + CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * length); + int *uniChars = (int *)malloc(sizeof(int) * length); + CGSize *advances = (CGSize *)malloc(sizeof(CGSize) * length); + + if (glyphs == NULL || advances == NULL) + { + (*env)->DeleteLocalRef(env, glyphsArray); + [NSException raise:NSMallocException format:@"%s-%s:%d", __FILE__, __FUNCTION__, __LINE__]; + return; + } + + doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray); + + free(glyphs); + free(uniChars); + free(advances); + } + + (*env)->DeleteLocalRef(env, glyphsArray); +} + +// Setup and save the state of the CGContext, and apply any java.awt.Font transforms to the context. +static inline void doDrawGlyphsPipe_applyFontTransforms +(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, const jfloat x, const jfloat y) +{ + CGContextRef cgRef = qsdo->cgRef; + CGContextSetFontSize(cgRef, 1.0); + CGContextSetFont(cgRef, strike->fAWTFont->fNativeCGFont); + CGContextSetTextMatrix(cgRef, CGAffineTransformIdentity); + + CGAffineTransform tx = strike->fFontTx; + tx.tx += x; + tx.ty += y; + CGContextConcatCTM(cgRef, tx); + + doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc(env, qsdo, strike, gVector); +} + + +#pragma mark --- CTextPipe JNI --- + + +/* + * Class: sun_lwawt_macosx_CTextPipe + * Method: doDrawString + * Signature: (Lsun/java2d/SurfaceData;JLjava/lang/String;DD)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doDrawString +(JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jstring str, jdouble x, jdouble y) +{ + QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata); + AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); + +JNF_COCOA_ENTER(env); + + jsize len = (*env)->GetStringLength(env, str); + + if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) // optimized for stack allocation <rdar://problem/4285041> + { + jchar unichars[len]; + (*env)->GetStringRegion(env, str, 0, len, unichars); + JNF_CHECK_AND_RETHROW_EXCEPTION(env); + + // Draw the text context + DrawTextContext(env, qsdo, awtStrike, unichars, len, x, y); + } + else + { + // Get string to draw and the length + const jchar *unichars = JNFGetStringUTF16UniChars(env, str); + + // Draw the text context + DrawTextContext(env, qsdo, awtStrike, unichars, len, x, y); + + JNFReleaseStringUTF16UniChars(env, str, unichars); + } + +JNF_COCOA_RENDERER_EXIT(env); +} + + +/* + * Class: sun_lwawt_macosx_CTextPipe + * Method: doUnicodes + * Signature: (Lsun/java2d/SurfaceData;J[CIIFF)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doUnicodes +(JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jcharArray unicodes, jint offset, jint length, jfloat x, jfloat y) +{ + QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata); + AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); + +JNF_COCOA_ENTER(env); + + // Setup the text context + if (length < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) // optimized for stack allocation + { + jchar copyUnichars[length]; + (*env)->GetCharArrayRegion(env, unicodes, offset, length, copyUnichars); + JNF_CHECK_AND_RETHROW_EXCEPTION(env); + DrawTextContext(env, qsdo, awtStrike, copyUnichars, length, x, y); + } + else + { + jchar *copyUnichars = malloc(length * sizeof(jchar)); + if (!copyUnichars) { + [JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory to create the glyphs for string drawing"]; + } + + @try { + (*env)->GetCharArrayRegion(env, unicodes, offset, length, copyUnichars); + JNF_CHECK_AND_RETHROW_EXCEPTION(env); + DrawTextContext(env, qsdo, awtStrike, copyUnichars, length, x, y); + } @finally { + free(copyUnichars); + } + } + +JNF_COCOA_RENDERER_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CTextPipe + * Method: doOneUnicode + * Signature: (Lsun/java2d/SurfaceData;JCFF)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doOneUnicode +(JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jchar aUnicode, jfloat x, jfloat y) +{ + QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata); + AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); + +JNF_COCOA_ENTER(env); + + DrawTextContext(env, qsdo, awtStrike, &aUnicode, 1, x, y); + +JNF_COCOA_RENDERER_EXIT(env); +} + +/* + * Class: sun_lwawt_macosx_CTextPipe + * Method: doDrawGlyphs + * Signature: (Lsun/java2d/SurfaceData;JLjava/awt/font/GlyphVector;FF)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doDrawGlyphs +(JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jobject gVector, jfloat x, jfloat y) +{ + QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata); + AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); + +JNF_COCOA_ENTER(env); + + qsdo->BeginSurface(env, qsdo, SD_Text); + if (qsdo->cgRef == NULL) + { + qsdo->FinishSurface(env, qsdo); + return; + } + + CGContextSaveGState(qsdo->cgRef); + JRSFontSetRenderingStyleOnContext(qsdo->cgRef, JRSFontGetRenderingStyleForHints(sun_awt_SunHints_INTVAL_FRACTIONALMETRICS_ON, sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON)); + + doDrawGlyphsPipe_applyFontTransforms(env, qsdo, awtStrike, gVector, x, y); + + CGContextRestoreGState(qsdo->cgRef); + + qsdo->FinishSurface(env, qsdo); + +JNF_COCOA_RENDERER_EXIT(env); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/ImageSurfaceData.h Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2011, 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. + */ + +#import "QuartzSurfaceData.h" +#import <pthread.h> + +typedef UInt8 Pixel8bit; +typedef UInt16 Pixel16bit; +typedef UInt32 Pixel32bit; + +typedef struct _ImageSDOps ImageSDOps; + +ImageSDOps* LockImage(JNIEnv* env, jobject imageSurfaceData); +void UnlockImage(JNIEnv* env, ImageSDOps* isdo); +ImageSDOps* LockImagePixels(JNIEnv* env, jobject imageSurfaceData); +void UnlockImagePixels(JNIEnv* env, ImageSDOps* isdo); + +// if there is no image created for isdo.imgRef, it creates and image using the isdo.dataProvider +// If there is an image present, this is a no-op +void makeSureImageIsCreated(ImageSDOps* isdo); + +struct _ContextInfo +{ + BOOL useWindowContextReference; + BOOL canUseJavaPixelsAsContext; + size_t bitsPerComponent; + size_t bytesPerPixel; + size_t bytesPerRow; + CGImageAlphaInfo alphaInfo; + CGColorSpaceRef colorSpace; +} +typedef ContextInfo; + +struct _ImageInfo +{ + size_t bitsPerComponent; + size_t bitsPerPixel; + size_t bytesPerPixel; + size_t bytesPerRow; + CGImageAlphaInfo alphaInfo; + CGColorSpaceRef colorSpace; +} +typedef ImageInfo; + +struct _ImageSDOps +{ + QuartzSDOps qsdo; // must be the first entry! + + ContextInfo contextInfo; + ImageInfo imageInfo; + BOOL isSubImage; + + jint* javaImageInfo; + + // parameters specifying this BufferedImage given to us from Java + jobject array; + jint offset; + jint width; + jint height; + jint javaPixelBytes; + jint javaPixelsBytesPerRow; + jobject icm; + jint type; + + Pixel8bit* pixels; + Pixel8bit* pixelsLocked; + + // needed by TYPE_BYTE_INDEXED + UInt16* indexedColorTable; + UInt32* lutData; + UInt32 lutDataSize; + + // Used as a cached image ref created from the isdo.dataprovider. This is only a chached image, and it might become invalid + // if somebody draws on the bitmap context, or the pixels are changed in java. In that case, we need to NULL out + // this image and recreate it from the data provider. + CGImageRef imgRef; + + // Cached instance of CGDataProvider. dataProvider is alloced the first time a bitmap context is created, providing the + // native pixels as a source of the data. The dataProviders life cycle is the same as ISDO. The reference gets + // released when we are done with the ISDO. + CGDataProviderRef dataProvider; + + // Pointer in memory that is used for create the CGBitmapContext and the CGDataProvider (used for imgRef). This is a native + // copy of the pixels for the Image. There is a spearate copy of the pixels that lives in Java heap. There are two main + // reasons why we keep those pixels spearate: 1) CG doesn't support all the Java pixel formats 2) The Garbage collector can + // move the java pixels at any time. There are possible workarounds for both problems. Number 2) seems to be a more serious issue, since + // we can solve 1) by only supporting certain image types. + void * nativePixels; + NSGraphicsContext* nsRef; + + pthread_mutex_t lock; + jint nrOfPixelsOwners; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/ImageSurfaceData.m Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,2002 @@ +/* + * Copyright (c) 2011, 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. + */ + +#import "ImageSurfaceData.h" + +#import "java_awt_Transparency.h" +#import "java_awt_image_BufferedImage.h" +#import "sun_awt_image_BufImgSurfaceData.h" +#import "sun_java2d_OSXOffScreenSurfaceData.h" + +#import "jni_util.h" +#import <JavaNativeFoundation/JavaNativeFoundation.h> + +#import "BufImgSurfaceData.h" +#import "ThreadUtilities.h" + + + +//#define DEBUG 1 +#if defined DEBUG + #define IMAGE_SURFACE_INLINE + #define PRINT(msg) {fprintf(stderr, "%s\n", msg);fflush(stderr);} +#else + #define IMAGE_SURFACE_INLINE static inline + #define PRINT(msg) {} +#endif + +// same value as defined in Sun's own code +#define XOR_ALPHA_CUTOFF 128 + +// for vImage framework headers +#include <Accelerate/Accelerate.h> + + +// private Quartz routines needed here +CG_EXTERN void CGContextSetCTM(CGContextRef ref, CGAffineTransform tx); + +static ContextInfo sDefaultContextInfo[sun_java2d_OSXOffScreenSurfaceData_TYPE_3BYTE_RGB+1] = +{ + {YES, YES, 8, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_CUSTOM // special case + {YES, YES, 8, 4, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_INT_RGB + {YES, YES, 8, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_INT_ARGB + {YES, YES, 8, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_INT_ARGB_PRE + {YES, YES, 8, 4, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_INT_BGR + {YES, NO, 8, 4, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_3BYTE_BGR // use the default ARGB_PRE context synce we have to sync by hand anyway + {YES, YES, 8, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_4BYTE_ABGR + {YES, YES, 8, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_4BYTE_ABGR_PRE +#ifdef __LITTLE_ENDIAN__ + {YES, YES, 5, 2, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder16Host, NULL}, // TYPE_USHORT_565_RGB + {YES, YES, 5, 2, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder16Host, NULL}, // TYPE_USHORT_555_RGB +#else + {YES, YES, 5, 2, 0, kCGImageAlphaNoneSkipFirst, NULL}, // TYPE_USHORT_565_RGB + {YES, YES, 5, 2, 0, kCGImageAlphaNoneSkipFirst, NULL}, // TYPE_USHORT_555_RGB +#endif + {YES, YES, 8, 1, 0, kCGImageAlphaNone, NULL}, // TYPE_BYTE_GRAY + {YES, NO, 8, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_USHORT_GRAY // use the default ARGB_PRE context synce we have to sync by hand anyway + {NO, NO, 8, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_BYTE_BINARY mapped to TYPE_CUSTOM + {YES, NO, 8, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_BYTE_INDEXED // use the default ARGB_PRE context synce we have to sync by hand anyway + {YES, NO, 8, 4, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_3BYTE_RGB +}; + +static ImageInfo sDefaultImageInfo[sun_java2d_OSXOffScreenSurfaceData_TYPE_3BYTE_RGB+1] = +{ + {8, 32, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_CUSTOM + {8, 32, 4, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_INT_RGB + {8, 32, 4, 0, kCGImageAlphaFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_INT_ARGB + {8, 32, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_INT_ARGB_PRE + {8, 32, 4, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_INT_BGR + {8, 32, 4, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_3BYTE_BGR + {8, 32, 4, 0, kCGImageAlphaFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_4BYTE_ABGR + {8, 32, 4, 0, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_4BYTE_ABGR_PRE +#ifdef __LITTLE_ENDIAN__ + {5, 16, 2, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder16Host, NULL}, // TYPE_USHORT_565_RGB + {5, 16, 2, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder16Host, NULL}, // TYPE_USHORT_555_RGB +#else + {5, 16, 2, 0, kCGImageAlphaNoneSkipFirst, NULL}, // TYPE_USHORT_565_RGB + {5, 16, 2, 0, kCGImageAlphaNoneSkipFirst, NULL}, // TYPE_USHORT_555_RGB +#endif + {8, 8, 1, 0, kCGImageAlphaNone, NULL}, // TYPE_BYTE_GRAY + {16, 16, 2, 0, kCGImageAlphaNone | kCGBitmapByteOrder16Host, NULL}, // TYPE_USHORT_GRAY + {0, 0, 0, 0, -1, NULL}, // TYPE_BYTE_BINARY mapped to TYPE_CUSTOM + {8, 32, 4, 0, kCGImageAlphaFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_BYTE_INDEXED // Fully OPAQUE INDEXED images will use kCGImageAlphaNoneSkipFirst for performance reasosn. see <rdar://4224874> + {8, 32, 4, 0, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, NULL}, // TYPE_3BYTE_RGB +}; + +static jfieldID rgbID; +static jfieldID mapSizeID; +static jfieldID CMpDataID; +static jfieldID allGrayID; + + +static JNF_CLASS_CACHE(jc_OSXOffScreenSurfaceData, "sun/java2d/OSXOffScreenSurfaceData"); +static JNF_MEMBER_CACHE(jm_syncFromCustom, jc_OSXOffScreenSurfaceData, "syncFromCustom", "()V"); +static JNF_MEMBER_CACHE(jm_syncToCustom, jc_OSXOffScreenSurfaceData, "syncToCustom", "()V"); +static JNF_CLASS_CACHE(jc_BufferedImage, "java/awt/image/BufferedImage"); +static JNF_MEMBER_CACHE(jm_SurfaceData, jc_BufferedImage, "sData", "Lsun/java2d/SurfaceData;"); +static JNF_CLASS_CACHE(jc_IndexColorModel, "java/awt/image/IndexColorModel"); +static JNF_MEMBER_CACHE(jm_rgb, jc_IndexColorModel, "rgb", "[I"); +static JNF_MEMBER_CACHE(jm_transparency, jc_IndexColorModel, "transparency", "I"); +static JNF_MEMBER_CACHE(jm_transparent_index, jc_IndexColorModel, "transparent_index", "I"); + +CGColorSpaceRef gColorspaceRGB = NULL; +CGColorSpaceRef gColorspaceGray = NULL; + +IMAGE_SURFACE_INLINE void PrintImageInfo(ImageSDOps* isdo) +{ + fprintf(stderr, "\n"); + fprintf(stderr, "PrintImageInfo:\n"); + fprintf(stderr, "\t \n"); + //fprintf(stderr, "\t magicID=%d\n", (jint)isdo->magicID); + //fprintf(stderr, "\n"); + fprintf(stderr, "\t isdo=%p\n", isdo); + fprintf(stderr, "\t \n"); + fprintf(stderr, "\t contextInfo:\n"); + fprintf(stderr, "\t useWindowContextReference=%d\n", isdo->contextInfo.useWindowContextReference); + fprintf(stderr, "\t canUseJavaPixelsAsContext=%d\n", isdo->contextInfo.canUseJavaPixelsAsContext); + fprintf(stderr, "\t bitsPerComponent=%ld\n", (long)isdo->contextInfo.bitsPerComponent); + fprintf(stderr, "\t bytesPerPixel=%ld\n", (long)isdo->contextInfo.bytesPerPixel); + fprintf(stderr, "\t bytesPerRow=%ld\n", (long)isdo->contextInfo.bytesPerRow); + fprintf(stderr, "\t alphaInfo=%ld\n", (long)isdo->contextInfo.alphaInfo); + fprintf(stderr, "\t \n"); + fprintf(stderr, "\t imageInfo:\n"); + fprintf(stderr, "\t bitsPerComponent=%ld\n", (long)isdo->imageInfo.bitsPerComponent); + fprintf(stderr, "\t bitsPerPixel=%ld\n", (long)isdo->imageInfo.bitsPerPixel); + fprintf(stderr, "\t bytesPerPixel=%ld\n", (long)isdo->imageInfo.bytesPerPixel); + fprintf(stderr, "\t bytesPerRow=%ld\n", (long)isdo->imageInfo.bytesPerRow); + fprintf(stderr, "\t alphaInfo=%ld\n", (long)isdo->imageInfo.alphaInfo); + fprintf(stderr, "\t \n"); + fprintf(stderr, "\t isSubImage=%d\n", isdo->isSubImage); + fprintf(stderr, "\t \n"); + fprintf(stderr, "\t java info:\n"); + fprintf(stderr, "\t array=%p\n", isdo->array); + fprintf(stderr, "\t offset=%d\n", (int)isdo->offset); + fprintf(stderr, "\t width=%d\n", (int)isdo->width); + fprintf(stderr, "\t height=%d\n", (int)isdo->height); + fprintf(stderr, "\t javaPixelBytes=%d\n", (int)isdo->javaPixelBytes); + fprintf(stderr, "\t javaPixelsBytesPerRow=%d\n", (int)isdo->javaPixelsBytesPerRow); + fprintf(stderr, "\t icm=%p\n", isdo->icm); + fprintf(stderr, "\t type=%d\n", (int)isdo->type); + fprintf(stderr, "\n"); + fprintf(stderr, "\t cgRef=%p\n", isdo->qsdo.cgRef); + fprintf(stderr, "\t nsRef=%p\n", isdo->nsRef); + fprintf(stderr, "\n"); + fprintf(stderr, "\t pixelsLocked=%p\n", isdo->pixelsLocked); + fprintf(stderr, "\t pixels=%p\n", isdo->pixels); + fprintf(stderr, "\n"); + fprintf(stderr, "\t indexedColorTable=%p\n", isdo->indexedColorTable); + fprintf(stderr, "\t lutData=%p\n", isdo->lutData); + fprintf(stderr, "\t lutDataSize=%u\n", (unsigned)isdo->lutDataSize); + fprintf(stderr, "\n"); + fprintf(stderr, "\t nrOfPixelsOwners=%u\n", (unsigned)isdo->nrOfPixelsOwners); + fprintf(stderr, "\n"); +} + +// if there is no image created for isdo.imgRef, it creates and image using the isdo.dataProvider +// If there is an image present, this is a no-op +void makeSureImageIsCreated(ImageSDOps* isdo) +{ + if (isdo->imgRef == NULL) // create the image + { + isdo->imgRef = CGImageCreate(isdo->width, + isdo->height, + isdo->contextInfo.bitsPerComponent, + isdo->contextInfo.bytesPerPixel * 8, + isdo->contextInfo.bytesPerRow, + isdo->contextInfo.colorSpace, + isdo->contextInfo.alphaInfo, + isdo->dataProvider, + NULL, + NO, + kCGRenderingIntentDefault); + } +} + +IMAGE_SURFACE_INLINE void customPixelsFromJava(JNIEnv *env, ImageSDOps *isdo) +{ +PRINT(" customPixelsFromJava") + + SurfaceDataOps *sdo = (SurfaceDataOps*)isdo; + JNFCallVoidMethod([ThreadUtilities getJNIEnv], sdo->sdObject, jm_syncFromCustom); // AWT_THREADING Safe (known object) +} + + +IMAGE_SURFACE_INLINE void copyBits(jint w, jint h, jint javaPixelsBytesPerRow, Pixel8bit *pixelsSrc, jint dstPixelsBytesPerRow, Pixel8bit *pixelsDst) +{ +PRINT(" copyBits") + + if (javaPixelsBytesPerRow == dstPixelsBytesPerRow) + { + memcpy(pixelsDst, pixelsSrc, h*javaPixelsBytesPerRow); + } + else + { + register jint y; + for (y=0; y<h; y++) + { + memcpy(pixelsDst, pixelsSrc, dstPixelsBytesPerRow); + + pixelsSrc += javaPixelsBytesPerRow; + pixelsDst += dstPixelsBytesPerRow; + } + } +} + +IMAGE_SURFACE_INLINE void copySwapRandB_32bit_TYPE_4BYTE(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel32bit *pixelsSrc, Pixel32bit *pixelsDst, size_t extraBytesPerRow) +{ +PRINT(" copySwapRandB_32bit_TYPE_4BYTE") + + register Pixel8bit *p8Bit = NULL; + register jint skip = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register Pixel32bit pixel, red, blue; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc++; + +#ifdef __LITTLE_ENDIAN__ + pixel = CFSwapInt32BigToHost(pixel); // the jint is in big endian format, we need to swap the bits +#endif + + red = (pixel & 0x00ff0000) >> 16; // get original red and shift to new position + blue = (pixel & 0x000000ff) << 16; // get original blue and shift to new position + + pixel = (pixel & 0xff00ff00); // erase original red&blue + + pixel = pixel | red | blue; // construct new pixel + + *pixelsDst++ = pixel; + } + pixelsSrc += skip; + + p8Bit = (Pixel8bit *) pixelsDst; + p8Bit += extraBytesPerRow; + pixelsDst = (Pixel32bit *) p8Bit; + } +} + + +IMAGE_SURFACE_INLINE void copySwapRandB_32bit_TYPE_INT(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel32bit *pixelsSrc, Pixel32bit *pixelsDst, size_t extraBytesPerRow) +{ +PRINT(" copySwapRandB_32bit_TYPE_INT") + + register Pixel8bit *p8Bit = NULL; + register jint skip = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register Pixel32bit pixel, red, blue; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc++; + + red = (pixel & 0x00ff0000) >> 16; // get original red and shift to new position + blue = (pixel & 0x000000ff) << 16; // get original blue and shift to new position + + pixel = (pixel & 0xff00ff00); // erase original red&blue + + pixel = pixel | red | blue; // construct new pixel + + *pixelsDst++ = pixel; + } + pixelsSrc += skip; + + p8Bit = (Pixel8bit *) pixelsDst; + p8Bit += extraBytesPerRow; + pixelsDst = (Pixel32bit *) p8Bit; + } +} + + +IMAGE_SURFACE_INLINE void copyBGR_24bitToXRGB_32bit(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel8bit *pixelsSrc, Pixel32bit *pixelsDst, size_t extraBytesPerRow) +{ +PRINT(" copyBGR_24bitToXRGB_32bit") + + register Pixel8bit *p8Bit = NULL; + register jint skip = ((javaPixelsBytesPerRow/javaPixelBytes)-w)*javaPixelBytes; // in pixelsSrc units + register Pixel32bit red, green, blue, pixel; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc++; + blue = pixel << 0; + + pixel = *pixelsSrc++; + green = pixel << 8; + + pixel = *pixelsSrc++; + red = pixel << 16; + + *pixelsDst = red | green | blue; + + *pixelsDst = 0xff000000 | *pixelsDst; + + pixelsDst++; + } + pixelsSrc += skip; + + p8Bit = (Pixel8bit *) pixelsDst; + p8Bit += extraBytesPerRow; + pixelsDst = (Pixel32bit *) p8Bit; + } +} + +IMAGE_SURFACE_INLINE void copyRGB_24bitToXRGB_32bit(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel8bit *pixelsSrc, Pixel32bit *pixelsDst, size_t extraBytesPerRow) +{ +PRINT(" copyRGB_24bitToXRGB_32bit") + + register Pixel8bit *p8Bit = NULL; + register jint skip = ((javaPixelsBytesPerRow/javaPixelBytes)-w)*javaPixelBytes; // in pixelsSrc units + register Pixel32bit red, green, blue, pixel; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc++; + red = pixel << 16; + + pixel = *pixelsSrc++; + green = pixel << 8; + + pixel = *pixelsSrc++; + blue = pixel << 0; + + *pixelsDst = red | green | blue; + + *pixelsDst = 0xff000000 | *pixelsDst; + + pixelsDst++; + } + pixelsSrc += skip; + + p8Bit = (Pixel8bit *) pixelsDst; + p8Bit += extraBytesPerRow; + pixelsDst = (Pixel32bit *) p8Bit; + } +} + +IMAGE_SURFACE_INLINE void copyIndexed_8bitToARGB_32bit(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel8bit *pixelsSrc, + Pixel32bit* lutdata, Pixel32bit *pixelsDst, size_t extraBytesPerRow) +{ +PRINT(" copyIndexed_8bitToARGB_32bit") + + //gznote: how is the performance if the extraBytesPerRow != 0 ? + const vImage_Buffer src = {pixelsSrc, h, w, javaPixelsBytesPerRow}; + const vImage_Buffer dest = {pixelsDst, h, w, w*sizeof(Pixel32bit)+extraBytesPerRow}; + vImage_Error err = vImageLookupTable_Planar8toPlanarF(&src, &dest, (Pixel_F*)lutdata, kvImageDoNotTile); + if (err != kvImageNoError) + { + fprintf(stderr, "Error in copyIndexed_8bitToARGB_32bit: vImageLookupTable_Planar8toPlanarF returns %ld\n", (long)err); + register Pixel8bit *p8Bit = NULL; + register jint skip = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register jint x, y; + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + *pixelsDst++ = lutdata[*pixelsSrc++]; // case 1 + //*pixelsDst++ = *(lutdata + *pixelsSrc++); // case 2: at best ~1% better than case 1 + } + pixelsSrc += skip; + + p8Bit = (Pixel8bit *) pixelsDst; + p8Bit += extraBytesPerRow; + pixelsDst = (Pixel32bit *) p8Bit; + } + } +} + +IMAGE_SURFACE_INLINE void copy565_16bitTo555_16bit(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel16bit *pixelsSrc, Pixel16bit *pixelsDst, size_t extraBytesPerRow) +{ +PRINT(" copy565_16bitTo555_16bit") + + register Pixel8bit *p8Bit = NULL; + register jint skip = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register jint green; + register Pixel16bit pixel; + register jint x, y; + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc++; + + green = ((pixel >> 5) & 63); // rrrrrggggggbbbbb => shift 5 right = 00000rrrrrgggggg => and 63 = 0000000000gggggg + green = ((jint) (((CGFloat) green / 63.0f) * 31.0f)) & 31; // first normalize to value between 0 and 1 and then un-normalize to 5 bit (31 = 0000000000011111) + + *pixelsDst++ = ((pixel&0xf800)>>1) | (green << 5) | (pixel&0x01f); + } + pixelsSrc += skip; + + p8Bit = (Pixel8bit *) pixelsDst; + p8Bit += extraBytesPerRow; + pixelsDst = (Pixel16bit *) p8Bit; + } +} + + +IMAGE_SURFACE_INLINE void customPixelsToJava(JNIEnv *env, ImageSDOps *isdo) +{ +PRINT(" customPixelsToJava") + + SurfaceDataOps *sdo = (SurfaceDataOps*)isdo; + JNFCallVoidMethod([ThreadUtilities getJNIEnv], sdo->sdObject, jm_syncToCustom); // AWT_THREADING Safe (known object) +} + +IMAGE_SURFACE_INLINE void removeAlphaPre_32bit(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel32bit *pixelsSrc) +{ +PRINT(" removeAlphaPre_32bit") + + register jint skip = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register Pixel32bit pixel, alpha, red, green, blue; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc; + + alpha = (pixel >> 24) & 0xff; + + if (alpha != 0) + { + // get color components + red = (pixel >> 16) & 0xff; + green = (pixel >> 8) & 0xff; + blue = (pixel >> 0) & 0xff; + + // remove alpha pre + red = ((red * 0xff) + 0x7f) / alpha; + green = ((green * 0xff) + 0x7f) / alpha; + blue = ((blue * 0xff) + 0x7f) / alpha; + + // clamp + red = (red <= 0xff) ? red : 0xff; + green = (green <= 0xff) ? green : 0xff; + blue = (blue <= 0xff) ? blue : 0xff; + + *pixelsSrc++ = (alpha<<24) | (red<<16) | (green<<8) | blue; // construct new pixel + } + else + { + *pixelsSrc++ = 0; + } + } + + pixelsSrc += skip; + } +} + +IMAGE_SURFACE_INLINE void swapRandBAndRemoveAlphaPre_32bit(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel32bit *pixelsSrc) +{ +PRINT(" swapRandBAndRemoveAlphaPre_32bit") + + register jint skip = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register Pixel32bit pixel, alpha, red, green, blue; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc; + + alpha = (pixel & 0xff000000) >> 24; + + if (alpha != 0) + { + // get color components + red = (pixel & 0x00ff0000) >> 16; + green = (pixel & 0x0000ff00) >> 8; + blue = (pixel & 0x000000ff) >> 0; + + // remove alpha pre + red = ((red * 0xff) + 0x7f) / alpha; + green = ((green * 0xff) + 0x7f) / alpha; + blue = ((blue * 0xff) + 0x7f) / alpha; + + // clamp + red = (red <= 0xff) ? red : 0xff; + green = (green <= 0xff) ? green : 0xff; + blue = (blue <= 0xff) ? blue : 0xff; + + pixel = (alpha<<24) | (blue<<16) | (green<<8) | red; // construct new pixel + +#ifdef __LITTLE_ENDIAN__ + pixel = CFSwapInt32HostToBig(pixel); // the jint is little endian, we need to swap the bits before we send it back to Java +#endif + + *pixelsSrc++ = pixel; + } + else + { + *pixelsSrc++ = 0; + } + } + + pixelsSrc += skip; + } +} + +IMAGE_SURFACE_INLINE void swapRandB_32bit_TYPE_INT(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel32bit *pixelsSrc) +{ +PRINT(" swapRandB_32bit_TYPE_INT") + + register jint skip = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register Pixel32bit pixel, red, blue; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc; + + red = (pixel & 0x00ff0000) >> 16; // get original red and shift to new position + blue = (pixel & 0x000000ff) << 16; // get original blue and shift to new position + + pixel = (pixel & 0xff00ff00); // erase original red&blue + + pixel = pixel | red | blue; // construct new pixel + + *pixelsSrc++ = pixel; + } + + pixelsSrc += skip; + } +} + +IMAGE_SURFACE_INLINE void swapRandB_32bit_TYPE_4BYTE(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel32bit *pixelsSrc) +{ +PRINT(" swapRandB_32bit_TYPE_4BYTE") + + register jint skip = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register Pixel32bit pixel, red, blue; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc; + + red = (pixel & 0x00ff0000) >> 16; // get original red and shift to new position + blue = (pixel & 0x000000ff) << 16; // get original blue and shift to new position + + pixel = (pixel & 0xff00ff00); // erase original red&blue + + pixel = pixel | red | blue; // construct new pixel + +#ifdef __LITTLE_ENDIAN__ + pixel = CFSwapInt32HostToBig(pixel); // the jint is little endian, we need to swap the bits before we send it back to Java +#endif + + *pixelsSrc++ = pixel; + } + + pixelsSrc += skip; + } +} + +IMAGE_SURFACE_INLINE void map555_16bitTo565_16bit(jint w, jint h, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel16bit *pixelsSrc) +{ +PRINT(" map555_16bitTo565_16bit") + register jint skip = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register jint green; + register Pixel16bit pixel; + register jint x, y; + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc; + + green = ((pixel >> 5) & 31); // rrrrrgggggbbbbb => shift 5 right = 000000rrrrrggggg => and 31 = 00000000000ggggg + green = ((jint) (((CGFloat) green / 31.0f) * 63.0f)) & 63; // first normalize between 0 and 1 and then un-normalize to 6 bit (63 = 0000000000111111) + + *pixelsSrc++ = ((pixel&0x7c00)<<1) | (green << 5) | (pixel&0x01f); + } + + pixelsSrc += skip; + } +} + +IMAGE_SURFACE_INLINE void copyARGB_PRE_32bitToBGR_24bit(jint w, jint h, jint nativePixelsBytesPerRow, Pixel32bit *pixelsSrc, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel8bit *pixelsDst) +{ +PRINT(" copyARGB_PRE_32bitToBGR_24bit") + + static const jint mask = 0x000000ff; + register jint skipSrc = (nativePixelsBytesPerRow/sizeof(Pixel32bit))-w; // in pixelsSrc units + register jint skipDst = ((javaPixelsBytesPerRow/javaPixelBytes)-w)*javaPixelBytes; // in pixelsDst units + register Pixel32bit pixel, alpha, red, green, blue; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc; + + alpha = (pixel >> 24) & mask; + + if (alpha != 0) + { + // extract color components + red = (pixel >> 16) & mask; + green = (pixel >> 8) & mask; + blue = (pixel >> 0) & mask; + + // remove alpha pre + red = ((red * 0xff) + 0x7f) / alpha; + green = ((green * 0xff) + 0x7f) / alpha; + blue = ((blue * 0xff) + 0x7f) / alpha; + + // clamp + *pixelsDst++ = (blue <= 0xff) ? blue : 0xff; + *pixelsDst++ = (green <= 0xff) ? green : 0xff; + *pixelsDst++ = (red <= 0xff) ? red : 0xff; + } + else + { + *pixelsDst++ = 0; // blue + *pixelsDst++ = 0; // green + *pixelsDst++ = 0; // red + } + + pixelsSrc++; + } + + pixelsSrc += skipSrc; + pixelsDst += skipDst; + } +} + + +IMAGE_SURFACE_INLINE void copyARGB_PRE_32bitToRGB_24bit(jint w, jint h, jint nativePixelsBytesPerRow, Pixel32bit *pixelsSrc, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel8bit *pixelsDst) +{ + PRINT(" copyARGB_PRE_32bitToRGB_24bit") + + static const jint mask = 0x000000ff; + register jint skipSrc = (nativePixelsBytesPerRow/sizeof(Pixel32bit))-w; // in pixelsSrc units + register jint skipDst = ((javaPixelsBytesPerRow/javaPixelBytes)-w)*javaPixelBytes; // in pixelsDst units + register Pixel32bit pixel, alpha, red, green, blue; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc; + + alpha = (pixel >> 24) & mask; + + if (alpha != 0) + { + // extract color components + red = (pixel >> 16) & mask; + green = (pixel >> 8) & mask; + blue = (pixel >> 0) & mask; + + // remove alpha pre + red = ((red * 0xff) + 0x7f) / alpha; + green = ((green * 0xff) + 0x7f) / alpha; + blue = ((blue * 0xff) + 0x7f) / alpha; + + // clamp + *pixelsDst++ = (red <= 0xff) ? red : 0xff; + *pixelsDst++ = (green <= 0xff) ? green : 0xff; + *pixelsDst++ = (blue <= 0xff) ? blue : 0xff; + } + else + { + *pixelsDst++ = 0; // blue + *pixelsDst++ = 0; // green + *pixelsDst++ = 0; // red + } + + pixelsSrc++; + } + + pixelsSrc += skipSrc; + pixelsDst += skipDst; + } +} + + +// gray = 0.3red + 0.59green + 0.11blue - NTSC standard (according to Luke Wallis) +IMAGE_SURFACE_INLINE void copyARGB_PRE_32bitToGray_16bit(jint w, jint h, jint nativePixelsBytesPerRow, Pixel32bit *pixelsSrc, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel16bit *pixelsDst) +{ +PRINT(" copyARGB_PRE_32bitToGray_16bit") + + static const jint mask = 0x000000ff; + register jint skipSrc = (nativePixelsBytesPerRow/sizeof(Pixel32bit))-w; // in pixelsSrc units + register jint skipDst = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsDst units + register Pixel32bit alpha; + register Pixel32bit pixel, red, green, blue; + register CGFloat pixelFloat; + register jint x, y; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + pixel = *pixelsSrc; + + // gznote: do we remove alpha pre here? + alpha = ((pixel >> 24) & mask); //extract + + if (alpha != 0) + { + red = ((pixel >> 16) & mask); // extract + green = ((pixel >> 8) & mask); // extract + blue = ((pixel >> 0) & mask); // extract + + alpha *= 0xff; // upsample to 16bit + red *= 0xff; // upsample to 16bit + green *= 0xff; // upsample to 16bit + blue *= 0xff; // upsample to 16bit + + red = ((red * 0xffff) + 0x7fff) / alpha; // remove alpha pre + red = (red <= 0xffff) ? red : 0xffff; + green = ((green * 0xffff) + 0x7fff) / alpha; // remove alpha pre + green = (green <= 0xffff) ? green : 0xffff; + blue = ((blue * 0xffff) + 0x7fff) / alpha; // remove alpha pre + blue = (blue <= 0xffff) ? blue : 0xffff; + + pixelFloat = red*0.3f + green*0.59f + blue*0.11f; // rgb->gray NTSC conversion + } + else + { + pixelFloat = 0; + } + + *pixelsDst = (jint)pixelFloat; + pixelsDst++; + + pixelsSrc++; + } + + pixelsSrc += skipSrc; + pixelsDst += skipDst; + } +} + +// 1. first "dither" the true color down by creating a 16 bit value of the real color that will serve as an index into the cache of indexes +// 2. if the cache has a valid entry use it otherwise go through 3 and 4 +// 3. go through the color table and calculate Euclidian distance between the true color and the indexed colors +// 4. map the shortest distance into the one and true index color and stick it into the dst (and cache) +IMAGE_SURFACE_INLINE UInt16* copyARGB_PRE_bitToIndexed_8bit(jint w, jint h, jint nativePixelsBytesPerRow, Pixel32bit *pixelsSrc, jint javaPixelsBytesPerRow, jint javaPixelBytes, Pixel8bit *pixelsDst, Pixel32bit* lutdata, UInt32 lutDataSize, UInt16 *indexedColorTable) +{ +PRINT(" copyARGB_PRE_bitToIndexed_8bit") + static const UInt32 mask = 0x000000ff; + + static const UInt32 indexSize = 65536; // 2^16 - 16 bits of precision + static const UInt32 indexMask = 0x000000f0; // 00000000000000000000000011110000 + static const UInt16 invalidIndex = 0xffff; // 1111111111111111 + + register jint skipSrc = (nativePixelsBytesPerRow/sizeof(Pixel32bit))-w; // in pixelsSrc units + register jint skipDst = (javaPixelsBytesPerRow/javaPixelBytes)-w; // in pixelsSrc units + register jint indexOfBest, indexOfBestCached = -1; + register CGFloat distanceOfBest, distance; + register UInt32 p1, p1Cached = 0, p1a, p1r, p1g, p1b, p2; + register SInt32 da, dr, dg, db; + register jint x, y, i; + BOOL cachedValueReady = NO; + + if (indexedColorTable == NULL) + { + indexedColorTable = (UInt16*)malloc(indexSize*sizeof(UInt16)); // 15 bit precision, each entry capable of holding a 2 byte value + // (lower byte for the actual index, higher byte to mark it valid/invalid) + + if (indexedColorTable != NULL) + { + memset((void*)indexedColorTable, invalidIndex, indexSize*sizeof(UInt16)); + } + else + { + fprintf(stderr, "ERROR: malloc returns NULL for isdo->indexedColorTable in copyARGB_PRE_bitToIndexed_8bit"); + return NULL; + } + } + + register UInt16 cacheIndex; + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + p1 = *pixelsSrc; + + if ((p1Cached != p1) || (cachedValueReady == NO)) + { + p1a = ((p1 >> 24) & mask); + + if (p1a != 0) + { + // extract color components + p1r = ((p1 >> 16) & mask); + p1g = ((p1 >> 8) & mask); + p1b = ((p1 >> 0) & mask); + + // remove alpha pre + p1r = ((p1r * 0xff) + 0x7f) / p1a; + p1g = ((p1g * 0xff) + 0x7f) / p1a; + p1b = ((p1b * 0xff) + 0x7f) / p1a; + + // clamp + p1r = (p1r <= 0xff) ? p1r : 0xff; + p1g = (p1g <= 0xff) ? p1g : 0xff; + p1b = (p1b <= 0xff) ? p1b : 0xff; + } + else + { + p1r = 0; + p1g = 0; + p1b = 0; + } + + cacheIndex = (UInt16)(((p1a & indexMask) << 8) | ((p1r & indexMask) << 4) | ((p1g & indexMask) << 0) | ((p1b & indexMask) >> 4)); + if (indexedColorTable[cacheIndex] == invalidIndex) + { + indexOfBest = 0; + distanceOfBest = DBL_MAX; + + for (i=0; i<lutDataSize; i++) + { + p2 = lutdata[i]; + + da = p1a - ((p2 >> 24) & mask); + dr = p1r - ((p2 >> 16) & mask); + dg = p1g - ((p2 >> 8) & mask); + db = p1b - ((p2 >> 0) & mask); + + distance = sqrt((da*da)+(dr*dr)+(dg*dg)+(db*db)); + if (distance < distanceOfBest) + { + distanceOfBest = distance; + indexOfBest = i; + } + } + + indexedColorTable[cacheIndex] = indexOfBest; + } + else + { + indexOfBest = indexedColorTable[cacheIndex]; + } + + cachedValueReady = YES; + p1Cached = p1; + indexOfBestCached = indexOfBest; + } + else + { + indexOfBest = indexOfBestCached; + } + + *pixelsDst = indexOfBest; + + pixelsDst++; + pixelsSrc++; + } + pixelsSrc += skipSrc; + pixelsDst += skipDst; + } + + return indexedColorTable; +} + +// callback from CG telling us it's done with the data. <rdar://problem/4762033> +static void releaseDataFromProvider(void *info, const void *data, size_t size) +{ + if (data != NULL) + { + free(data); + } +} + +IMAGE_SURFACE_INLINE void createContext(JNIEnv *env, ImageSDOps *isdo) +{ +PRINT("createContext") + + QuartzSDOps *qsdo = (QuartzSDOps*)isdo; + if (qsdo->cgRef == NULL) // lazy creation + { + size_t bitsPerComponent = isdo->contextInfo.bitsPerComponent; + CGColorSpaceRef colorSpace = isdo->contextInfo.colorSpace; + CGImageAlphaInfo alphaInfo = isdo->contextInfo.alphaInfo; + + size_t bytesPerRow = isdo->contextInfo.bytesPerRow; + size_t size = bytesPerRow * isdo->height; + isdo->nativePixels = malloc(size); + + if (isdo->nativePixels == NULL) + { + fprintf(stderr, "malloc failed for size %d bytes in ImageSurfaceData.createContext()\n", (int) size); + } + +//fprintf(stderr, "isdo=%p isdo->type=%d, bitsPerComponent=%d, bytesPerRow=%d, colorSpace=%p, alphaInfo=%d, width=%d, height=%d, size=%d\n", isdo, type, (jint)bitsPerComponent, (jint)bytesPerRow, colorSpace, (jint)alphaInfo, (jint) isdo->width, (jint) isdo->height, (jint) size); + + qsdo->cgRef = CGBitmapContextCreate(isdo->nativePixels, isdo->width, isdo->height, bitsPerComponent, bytesPerRow, colorSpace, alphaInfo); + isdo->dataProvider = CGDataProviderCreateWithData(NULL, isdo->nativePixels, size, releaseDataFromProvider); + } + +//fprintf(stderr, "cgRef=%p\n", qsdo->cgRef); + if (qsdo->cgRef == NULL) + { + fprintf(stderr, "ERROR: (qsdo->cgRef == NULL) in createContext!\n"); + } + + // intitalize the context to match the Java coordinate system + + // BG, since the context is created above, we can just concat + //CGContextSetCTM(qsdo->cgRef, CGAffineTransformMake(1, 0, 0, -1, 0, isdo->height)); + CGContextConcatCTM(qsdo->cgRef, CGAffineTransformMake(1, 0, 0, -1, 0, isdo->height)); + + CGContextSaveGState(qsdo->cgRef); // this will make sure we don't go pass device context settings + CGContextSaveGState(qsdo->cgRef); // this will put user settings on top, used by LazyStateManagement code + qsdo->newContext = YES; +} + +IMAGE_SURFACE_INLINE void holdJavaPixels(JNIEnv* env, ImageSDOps* isdo) +{ +PRINT("holdJavaPixels") + + if (isdo->type != java_awt_image_BufferedImage_TYPE_CUSTOM) + { + Pixel8bit* pixels = NULL; + if (isdo->nrOfPixelsOwners == 0) + { + pixels = (Pixel8bit*)((*env)->GetPrimitiveArrayCritical(env, isdo->array, NULL)); + if (pixels != NULL) + { + isdo->pixelsLocked = pixels; + + isdo->pixels = isdo->pixelsLocked + isdo->offset; + } + else + { + fprintf(stderr, "ERROR: GetPrimitiveArrayCritical returns NULL for pixels in holdJavaPixels!\n"); + } + } + isdo->nrOfPixelsOwners++; + } + else if (isdo->pixels == NULL) + { + isdo->pixels = (Pixel8bit*)((*env)->GetDirectBufferAddress(env, isdo->array)); + } +} + +IMAGE_SURFACE_INLINE void unholdJavaPixels(JNIEnv* env, ImageSDOps* isdo) +{ +PRINT("unholdJavaPixels") + + if (isdo->type != java_awt_image_BufferedImage_TYPE_CUSTOM) + { + isdo->nrOfPixelsOwners--; + if (isdo->nrOfPixelsOwners == 0) + { + isdo->pixels = NULL; + + (*env)->ReleasePrimitiveArrayCritical(env, isdo->array, isdo->pixelsLocked, 0); // Do not use JNI_COMMIT, as that will not free the buffer copy when +ProtectJavaHeap is on. + isdo->pixelsLocked = NULL; + } + } +} + +static void imageDataProvider_UnholdJavaPixels(void *info, const void *data, size_t size) +{ +PRINT("imageDataProvider_UnholdJavaPixels") + + ImageSDOps* isdo = (ImageSDOps*)info; + unholdJavaPixels([ThreadUtilities getJNIEnv], isdo); +} +static void imageDataProvider_FreeTempPixels(void *info, const void *data, size_t size) +{ +PRINT("imageDataProvider_FreeTempPixels") + + free((void *)data); +} +IMAGE_SURFACE_INLINE void syncFromJavaPixels(JNIEnv* env, ImageSDOps* isdo) +{ +PRINT("syncFromJavaPixels") + + // check to see if we have any work to do + if (isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] == 1) + { + // if we do, lock down Java pixels, this halts GarbageCollector! + holdJavaPixels(env, isdo); + if (isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] == 1) + { + isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] = 0; + + void *dataProviderData = NULL; + void *dataProviderInfo = NULL; + void *dataProviderCallback = NULL; + size_t dataProviderDataSize = 0; + size_t width = isdo->width; + size_t height = isdo->height; + size_t bitsPerComponent = isdo->imageInfo.bitsPerComponent; + size_t bitsPerPixel = isdo->imageInfo.bitsPerPixel; + size_t bytesPerRow = 0; + size_t extraBytesPerRow = 0; // these are the extra bytesPerRow used for alignement + + switch (isdo->type) + { + //case java_awt_image_BufferedImage_TYPE_BYTE_BINARY: // mapped to TYPE_CUSTOM + case java_awt_image_BufferedImage_TYPE_CUSTOM: + holdJavaPixels(env, isdo); // we lock again since we are reusing pixels, but we must ensure CGImageRef immutability + // we can lock these pixels down because they are nio based, so we don't halt the GarbageCollector + bytesPerRow = isdo->javaPixelsBytesPerRow; + dataProviderDataSize = bytesPerRow*isdo->height; + dataProviderData = isdo->pixels; + dataProviderInfo = isdo; + dataProviderCallback = imageDataProvider_UnholdJavaPixels; + break; + default: + bytesPerRow = isdo->imageInfo.bytesPerRow; + dataProviderDataSize = bytesPerRow*height; + dataProviderData = malloc(dataProviderDataSize); + dataProviderInfo = isdo; + dataProviderCallback = imageDataProvider_FreeTempPixels; + } + + switch (isdo->type) + { + //case java_awt_image_BufferedImage_TYPE_BYTE_BINARY: // mapped to TYPE_CUSTOM + case java_awt_image_BufferedImage_TYPE_CUSTOM: + customPixelsFromJava(env, isdo); + break; + case java_awt_image_BufferedImage_TYPE_INT_RGB: + case java_awt_image_BufferedImage_TYPE_INT_ARGB: + case java_awt_image_BufferedImage_TYPE_INT_ARGB_PRE: + case java_awt_image_BufferedImage_TYPE_USHORT_555_RGB: + case java_awt_image_BufferedImage_TYPE_USHORT_GRAY: + case java_awt_image_BufferedImage_TYPE_BYTE_GRAY: + copyBits(width, height, isdo->javaPixelsBytesPerRow, (Pixel8bit*)isdo->pixels, bytesPerRow, dataProviderData); + break; + case java_awt_image_BufferedImage_TYPE_INT_BGR: + copySwapRandB_32bit_TYPE_INT(width, height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, (Pixel32bit*)isdo->pixels, dataProviderData, extraBytesPerRow); + break; + case java_awt_image_BufferedImage_TYPE_4BYTE_ABGR: + case java_awt_image_BufferedImage_TYPE_4BYTE_ABGR_PRE: + copySwapRandB_32bit_TYPE_4BYTE(width, height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, (Pixel32bit*)isdo->pixels, dataProviderData, extraBytesPerRow); + break; + case java_awt_image_BufferedImage_TYPE_3BYTE_BGR: + copyBGR_24bitToXRGB_32bit(width, height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, isdo->pixels, dataProviderData, extraBytesPerRow); + break; + case sun_java2d_OSXOffScreenSurfaceData_TYPE_3BYTE_RGB: + copyRGB_24bitToXRGB_32bit(width, height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, isdo->pixels, dataProviderData, extraBytesPerRow); + break; + case java_awt_image_BufferedImage_TYPE_USHORT_565_RGB: + copy565_16bitTo555_16bit(width, height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, (Pixel16bit*)isdo->pixels, dataProviderData, extraBytesPerRow); + break; + case java_awt_image_BufferedImage_TYPE_BYTE_INDEXED: + copyIndexed_8bitToARGB_32bit(width, height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, isdo->pixels, isdo->lutData, dataProviderData, extraBytesPerRow); + break; + default: + break; + } + + CGDataProviderRef provider = CGDataProviderCreateWithData(dataProviderInfo, dataProviderData, dataProviderDataSize, dataProviderCallback); + CGImageRef javaImg = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, + isdo->imageInfo.colorSpace, isdo->imageInfo.alphaInfo, provider, NULL, NO, kCGRenderingIntentDefault); +//fprintf(stderr, "javaImg=%p\n", javaImg); + CGDataProviderRelease(provider); + + if (javaImg != NULL) + { + QuartzSDOps *qsdo = (QuartzSDOps*)isdo; + + if (isdo->imgRef != NULL) + { + CGImageRelease(isdo->imgRef); + isdo->imgRef = NULL; + } + + if (qsdo->cgRef == NULL) + { + createContext(env, isdo); + } + + if (qsdo->cgRef != NULL) + { + CGContextSaveGState(qsdo->cgRef); + CGContextSetCTM(qsdo->cgRef, CGAffineTransformMake(1, 0, 0, 1, 0, 0)); + CGContextSetBlendMode(qsdo->cgRef, kCGBlendModeCopy); + CGContextSetAlpha(qsdo->cgRef, 1.0f); + CGContextDrawImage(qsdo->cgRef, CGRectMake(0, 0, width, height), javaImg); + CGContextFlush(qsdo->cgRef); + CGContextRestoreGState(qsdo->cgRef); + CGImageRelease(javaImg); + } + else + { + fprintf(stderr, "ERROR: (cgRef == NULL) in syncFromJavaPixels!\n"); + } + } + else + { +//fprintf(stderr, "isdo->type=%d, isdo->width=%d, isdo->height=%d, isdo->imageInfo.bitsPerComponent=%d, isdo->imageInfo.bytesPerPixel=%d, isdo->imageInfo.bitsPerPixel=%d, isdo->imageInfo.bytesPerRow=%d, isdo->imageInfo.colorSpace=%p, isdo->imageInfo.alphaInfo=%d\n", +//(jint)isdo->type, (jint)isdo->width, (jint)isdo->height, (jint)isdo->imageInfo.bitsPerComponent, (jint)isdo->imageInfo.bytesPerPixel, (jint)isdo->imageInfo.bitsPerPixel, (jint)isdo->imageInfo.bytesPerRow, isdo->imageInfo.colorSpace, (jint)isdo->imageInfo.alphaInfo); + fprintf(stderr, "ERROR: (javaImg == NULL) in syncFromJavaPixels!\n"); + } + } + + unholdJavaPixels(env, isdo); + } +} + +IMAGE_SURFACE_INLINE void processPixels(ImageSDOps* isdo, jint x, jint y, jint width, jint height, void (*processPixelsCallback) (ImageSDOps *, jint, Pixel32bit *, jint, jint, jint, jint)) +{ + processPixelsCallback(isdo, (jint) isdo->contextInfo.bytesPerRow, (Pixel32bit *) isdo->nativePixels, x, y, width, height); +} + +IMAGE_SURFACE_INLINE void syncToJavaPixels_processPixelsCallback(ImageSDOps* isdo, jint nativePixelsBytesPerRow, Pixel32bit *dataSrc, jint x, jint y, jint width, jint height) +{ + switch (isdo->type) + { + case java_awt_image_BufferedImage_TYPE_3BYTE_BGR: + copyARGB_PRE_32bitToBGR_24bit(isdo->width, isdo->height, nativePixelsBytesPerRow, dataSrc, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, isdo->pixels); + break; + case sun_java2d_OSXOffScreenSurfaceData_TYPE_3BYTE_RGB: + copyARGB_PRE_32bitToRGB_24bit(isdo->width, isdo->height, nativePixelsBytesPerRow, dataSrc, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, isdo->pixels); + break; + case java_awt_image_BufferedImage_TYPE_USHORT_GRAY: + copyARGB_PRE_32bitToGray_16bit(isdo->width, isdo->height, nativePixelsBytesPerRow, dataSrc, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, (Pixel16bit*)isdo->pixels); + break; + case java_awt_image_BufferedImage_TYPE_BYTE_INDEXED: + isdo->indexedColorTable = copyARGB_PRE_bitToIndexed_8bit(isdo->width, isdo->height, nativePixelsBytesPerRow, dataSrc, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, isdo->pixels, isdo->lutData, isdo->lutDataSize, isdo->indexedColorTable); + break; + default: + break; + } +} + + +IMAGE_SURFACE_INLINE void syncToJavaPixels(JNIEnv* env, ImageSDOps* isdo) +{ +PRINT("syncToJavaPixels") + + holdJavaPixels(env, isdo); + + QuartzSDOps *qsdo = (QuartzSDOps*)isdo; + if (qsdo->cgRef == NULL) + { + createContext(env, isdo); + } + + isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNativePixelsChangedIndex] = 0; + + if (isdo->contextInfo.canUseJavaPixelsAsContext == YES) + { + + jint srcBytesPerRow = isdo->contextInfo.bytesPerRow; + jint dstBytesPerRow = isdo->javaPixelsBytesPerRow; + jint h = isdo->height; + Pixel8bit *pixelsSrc = isdo->nativePixels; + Pixel8bit *pixelsDst = isdo->pixels; + + if (srcBytesPerRow == dstBytesPerRow) + { + memcpy(pixelsDst, pixelsSrc, h * dstBytesPerRow); + } + else + { + jint widthInBytes = isdo->width * isdo->contextInfo.bytesPerPixel; + jint y; + for (y=0; y < h; y++) + { + memcpy(pixelsDst, pixelsSrc, widthInBytes); + + pixelsSrc += srcBytesPerRow; + pixelsDst += dstBytesPerRow; + } + } + + switch (isdo->type) + { + //case java_awt_image_BufferedImage_TYPE_BYTE_BINARY: // mapped to TYPE_CUSTOM + case java_awt_image_BufferedImage_TYPE_CUSTOM: + customPixelsToJava(env, isdo); + break; + case java_awt_image_BufferedImage_TYPE_INT_ARGB: + removeAlphaPre_32bit(isdo->width, isdo->height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, (Pixel32bit*)isdo->pixels); + break; + case java_awt_image_BufferedImage_TYPE_4BYTE_ABGR: + swapRandBAndRemoveAlphaPre_32bit(isdo->width, isdo->height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, (Pixel32bit*)isdo->pixels); + break; + case java_awt_image_BufferedImage_TYPE_INT_BGR: + swapRandB_32bit_TYPE_INT(isdo->width, isdo->height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, (Pixel32bit*)isdo->pixels); + break; + case java_awt_image_BufferedImage_TYPE_4BYTE_ABGR_PRE: + swapRandB_32bit_TYPE_4BYTE(isdo->width, isdo->height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, (Pixel32bit*)isdo->pixels); + break; + case java_awt_image_BufferedImage_TYPE_USHORT_565_RGB: + map555_16bitTo565_16bit(isdo->width, isdo->height, isdo->javaPixelsBytesPerRow, isdo->javaPixelBytes, (Pixel16bit*)isdo->pixels); + break; + default: + break; + } + } + else + { + processPixels(isdo, 0, 0, isdo->width, isdo->height, &syncToJavaPixels_processPixelsCallback); + } + + unholdJavaPixels(env, isdo); +} + + +IMAGE_SURFACE_INLINE jboolean xorSurfacePixels(JNIEnv *env, jobject dstIsd, jobject srcIsd, jint colorXOR, jint x, jint y, jint w, jint h) +{ +PRINT("xorSurfacePixels") + + jboolean handled = JNI_FALSE; + +JNF_COCOA_ENTER(env); + ImageSDOps* srcIsdo = LockImagePixels(env, srcIsd); + ImageSDOps* dstIsdo = LockImagePixels(env, dstIsd); + + if ((x < 0) || (y < 0) || (x+w > dstIsdo->width) || (y+h > dstIsdo->height) || (w > srcIsdo->width) || (h > srcIsdo->height)) + { +#ifdef PRINT_WARNINGS +fprintf(stderr, "xorSurfacePixels INVALID parameters: x=%d, y=%d, w=%d, h=%d\n", x, y, w, h); +fprintf(stderr, " dstIsdo->width=%d, dstIsdo->height=%d, biqsdoPixels->width=%d, biqsdoPixels->height=%d\n", + dstIsdo->width, dstIsdo->height, srcIsdo->width, srcIsdo->height); +#endif + UnlockImagePixels(env, srcIsdo); + UnlockImagePixels(env, dstIsdo); + + return JNI_FALSE; + } + + jint offset = (dstIsdo->width*y)+x; + register Pixel32bit* dstPixels = (Pixel32bit*)dstIsdo->pixels; + register jint skip = dstIsdo->width - w; + register Pixel32bit* srcPixels = (Pixel32bit*)srcIsdo->pixels; + register jint skipPixels = srcIsdo->width - w; + register jint i, j; + + dstPixels += offset; + + switch (dstIsdo->type) + { + case java_awt_image_BufferedImage_TYPE_INT_RGB: + case java_awt_image_BufferedImage_TYPE_INT_ARGB: + case java_awt_image_BufferedImage_TYPE_INT_ARGB_PRE: + { + dstIsdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] = 1; + + if (dstIsdo->type == java_awt_image_BufferedImage_TYPE_INT_ARGB_PRE) + { + Pixel8bit alpha = (colorXOR>>24)&0xff; + Pixel8bit red = (colorXOR>>16)&0xff; + red = (jint)(((CGFloat)red/255.0f * (CGFloat)alpha/255.0f)*255.0f); + Pixel8bit green = (colorXOR>>8)&0xff; + green = (jint)(((CGFloat)green/255.0f * (CGFloat)alpha/255.0f)*255.0f); + Pixel8bit blue = (colorXOR>>0)&0xff; + blue = (jint)(((CGFloat)blue/255.0f * (CGFloat)alpha/255.0f)*255.0f); + colorXOR = (alpha<<24) | (red<<16) | (green<<8) | blue; // the color is now alpha premultiplied + } + + for (i=0; i<h; i++) + { + for (j=0; j<w; j++) + { + Pixel32bit srcPixel = *srcPixels; + Pixel8bit pixelAlpha = (srcPixel>>24); + if (pixelAlpha > XOR_ALPHA_CUTOFF) + { + *dstPixels = (*dstPixels ^ (srcPixel ^ colorXOR)); + } + dstPixels++; srcPixels++; + } + + dstPixels += skip; + srcPixels += skipPixels; + } + + handled = JNI_TRUE; + break; + } + default: + { + handled = JNI_FALSE; +#if defined(PRINT_WARNINGS) + fprintf(stderr, "WARNING: unknown type (%d) in compositeXOR\n", dstIsdo->type); + PrintImageInfo(dstIsdo); +#endif + } + } + + UnlockImagePixels(env, srcIsdo); + UnlockImagePixels(env, dstIsdo); + +JNF_COCOA_EXIT(env); + return handled; +} + +IMAGE_SURFACE_INLINE jboolean clearSurfacePixels(JNIEnv *env, jobject bisd, jint w, jint h) +{ +PRINT("clearSurfacePixels") + jboolean handled = JNI_FALSE; + +JNF_COCOA_ENTER(env); + + ImageSDOps *isdo = LockImagePixels(env, bisd); + + if (isdo->type == java_awt_image_BufferedImage_TYPE_INT_ARGB_PRE) + { + isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] = 1; + + w = (w < isdo->width) ? w : isdo->width; + h = (h < isdo->height) ? h : isdo->height; + + register Pixel32bit* data = (Pixel32bit*)isdo->pixels; + register jint i; + if ((w < isdo->width) || (h < isdo->height)) //cmcnote: necessary to special-case for small height? wouldn't 4*w*h do it? + { + register jint skip = isdo->width; + register jint row = 4*w; + for (i=0; i<h; i++) + { + bzero(data, row); + data += skip; + } + } + else + { + bzero(data, 4*w*h); + } + + handled = JNI_TRUE; + } + UnlockImagePixels(env, isdo); + +JNF_COCOA_EXIT(env); + + return handled; +} + +static void ImageSD_startCGContext(JNIEnv *env, QuartzSDOps *qsdo, SDRenderType renderType) +{ +PRINT("ImageSD_startCGContext") + + ImageSDOps *isdo = (ImageSDOps*)qsdo; + + pthread_mutex_lock(&isdo->lock); + + if (isdo->imgRef != NULL) + { + CGImageRelease(isdo->imgRef); + isdo->imgRef = NULL; + } + + if (qsdo->cgRef == NULL) + { + createContext(env, isdo); + } + else + { + qsdo->newContext = NO; + } + + if (qsdo->cgRef != NULL) + { + if (isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kImageStolenIndex] == 1) + { + isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] = 1; + } + + // sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex can be set right above or somewhere else + if (isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] == 1) + { + syncFromJavaPixels(env, isdo); + } + + isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNativePixelsChangedIndex] = 1; + + SetUpCGContext(env, qsdo, renderType); + } +} +static void ImageSD_finishCGContext(JNIEnv *env, QuartzSDOps *qsdo) +{ +PRINT("ImageSD_finishCGContext") + + ImageSDOps *isdo = (ImageSDOps*)qsdo; + + if (qsdo->cgRef != NULL) + { + CompleteCGContext(env, qsdo); + + if (isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kImageStolenIndex] == 1) + { + syncToJavaPixels(env, isdo); + isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] = 1; + } + } + + pthread_mutex_unlock(&isdo->lock); +} + +static void ImageSD_dispose(JNIEnv *env, SurfaceDataOps *ops) +{ +PRINT("ImageSD_dispose") + + // copied from BufImg_Dispose in BufImgSurfaceData.c + { + /* ops is assumed non-null as it is checked in SurfaceData_DisposeOps */ + BufImgSDOps *bisdo = (BufImgSDOps *)ops; + (*env)->DeleteWeakGlobalRef(env, bisdo->array); + if (bisdo->lutarray != NULL) { + (*env)->DeleteWeakGlobalRef(env, bisdo->lutarray); + } + if (bisdo->icm != NULL) { + (*env)->DeleteWeakGlobalRef(env, bisdo->icm); + } + } + + QuartzSDOps *qsdo = (QuartzSDOps *)ops; + + if (qsdo->graphicsStateInfo.batchedLines != NULL) + { + free(qsdo->graphicsStateInfo.batchedLines); + qsdo->graphicsStateInfo.batchedLines = NULL; + } + + JNFDeleteGlobalRef(env, qsdo->javaGraphicsStatesObjects); + + if (qsdo->cgRef != NULL) + { + CGContextRelease(qsdo->cgRef); + qsdo->cgRef = NULL; + } + + ImageSDOps *isdo = (ImageSDOps *)ops; + + if (isdo->dataProvider != NULL) + { + CGDataProviderRelease(isdo->dataProvider); + isdo->dataProvider = NULL; + } + if (isdo->imgRef != NULL) + { + CGImageRelease(isdo->imgRef); + isdo->imgRef = NULL; + } + if (isdo->indexedColorTable != NULL) + { + free(isdo->indexedColorTable); + isdo->indexedColorTable = NULL; + } + if (isdo->lutData != NULL) + { + free(isdo->lutData); + isdo->indexedColorTable = NULL; + } + if (isdo->array != NULL) + { + JNFDeleteGlobalRef(env, isdo->array); + isdo->array = NULL; + } + if (isdo->icm != NULL) + { + JNFDeleteGlobalRef(env, isdo->icm); + isdo->icm = NULL; + } + + if (isdo->nsRef) { + CFRelease(isdo->nsRef); // GC + isdo->nsRef = nil; + } + + pthread_mutex_destroy(&isdo->lock); +} + +// used by XOR (Java pixels must be up to date) +ImageSDOps* LockImagePixels(JNIEnv* env, jobject imageSurfaceData) +{ +PRINT("LockImagePixels") + + ImageSDOps* isdo = (ImageSDOps*)SurfaceData_GetOps(env, imageSurfaceData); + + pthread_mutex_lock(&isdo->lock); + + holdJavaPixels(env, isdo); + + // if we need to access this image's pixels we need to convert native pixels (if any) back to Java + if (isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNativePixelsChangedIndex] == 1) + { + syncToJavaPixels(env, isdo); + isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] = 1; + } + + return isdo; +} +void UnlockImagePixels(JNIEnv* env, ImageSDOps* isdo) +{ +PRINT("UnlockImagePixels") + // don't do that since the native pixels haven't changed (Java pixels == native pixels) + //syncToJavaPixels(env, isdo); + + unholdJavaPixels(env, isdo); + + pthread_mutex_unlock(&isdo->lock); +} + +// used by drawImage (native pixels must be up to date) +ImageSDOps* LockImage(JNIEnv* env, jobject imageSurfaceData) +{ +PRINT("LockImage") + + ImageSDOps* isdo = (ImageSDOps*)SurfaceData_GetOps(env, imageSurfaceData); + + pthread_mutex_lock(&isdo->lock); + + // if we need to access this image's pixels we need to convert native pixels (if any) back to Java + // for those images whose context type doesn't match layer type or is a custom image + if (isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kImageStolenIndex] == 1) + { + isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] = 1; + } + + // sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex can be set right above or somewhere else + if (isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] == 1) + { + syncFromJavaPixels(env, isdo); + } + + return isdo; +} +void UnlockImage(JNIEnv* env, ImageSDOps* isdo) +{ +PRINT("UnlockImage") + + // don't do that since the native pixels haven't changed (Java pixels == native pixels) + //syncToJavaPixels(env, isdo); + + pthread_mutex_unlock(&isdo->lock); +} + +JNIEXPORT jobject JNICALL Java_sun_awt_image_BufImgSurfaceData_getSurfaceData + (JNIEnv *env, jclass bisd, jobject bufImg) +{ + static jfieldID sDataID = 0; + if (sDataID == 0) + { + static char *bimgName = "java/awt/image/BufferedImage"; + jclass bimg = (*env)->FindClass(env, bimgName); + sDataID = (*env)->GetFieldID(env, bimg, "sData", "Lsun/java2d/SurfaceData;"); + } + + return (*env)->GetObjectField(env, bufImg, sDataID); +} + +JNIEXPORT void JNICALL Java_sun_awt_image_BufImgSurfaceData_setSurfaceData + (JNIEnv *env, jclass bisd, jobject bufImg, jobject sData) +{ + static jfieldID sDataID = 0; + if (sDataID == 0) + { + static char *bimgName = "java/awt/image/BufferedImage"; + jclass bimg = (*env)->FindClass(env, bimgName); + sDataID = (*env)->GetFieldID(env, bimg, "sData", "Lsun/java2d/SurfaceData;"); + } + + (*env)->SetObjectField(env, bufImg, sDataID, sData); +} + +JNIEXPORT void JNICALL Java_sun_java2d_OSXOffScreenSurfaceData_initIDs(JNIEnv *env, jclass bisd) +{ +//PRINT("initIDs") + // copied from Java_sun_awt_image_BufImgSurfaceData_initIDs in BufImgSurfaceData.c + { + static char *icmName = "java/awt/image/IndexColorModel"; + jclass icm; + + if (sizeof(BufImgRIPrivate) > SD_RASINFO_PRIVATE_SIZE) { + JNU_ThrowInternalError(env, "Private RasInfo structure too large!"); + return; + } + + icm = (*env)->FindClass(env, icmName); + if (icm == NULL) { + return; + } + + rgbID = (*env)->GetFieldID(env, icm, "rgb", "[I"); + allGrayID = (*env)->GetFieldID(env, icm, "allgrayopaque", "Z"); + mapSizeID = (*env)->GetFieldID(env, icm, "map_size", "I"); + CMpDataID = (*env)->GetFieldID(env, icm, "pData", "J"); + if (allGrayID == 0 || rgbID == 0 || mapSizeID == 0 || CMpDataID == 0) { + JNU_ThrowInternalError(env, "Could not get field IDs"); + } + } + + gColorspaceRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + gColorspaceGray = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); +//fprintf(stderr, "gColorspaceRGB=%p, gColorspaceGray=%p\n", gColorspaceRGB, gColorspaceGray); +} + +JNIEXPORT jobject JNICALL Java_sun_java2d_OSXOffScreenSurfaceData_getSurfaceData + (JNIEnv *env, jclass bisd, jobject bufImg) +{ +PRINT("getSurfaceData") + + return JNFGetObjectField(env, bufImg, jm_SurfaceData); +} + +JNIEXPORT void JNICALL Java_sun_java2d_OSXOffScreenSurfaceData_setSurfaceData + (JNIEnv *env, jclass bisd, jobject bufImg, jobject sData) +{ +PRINT("setSurfaceData") + + JNFSetObjectField(env, bufImg, jm_SurfaceData, sData); +} + +static jint ImageSD_Lock(JNIEnv *env, SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo, jint lockflags) +{ + ImageSDOps *isdo = (ImageSDOps*)ops; + pthread_mutex_lock(&isdo->lock); + + // copied from BufImg_Lock in BufImgSurfaceData.c + { + BufImgSDOps *bisdo = (BufImgSDOps *)ops; + BufImgRIPrivate *bipriv = (BufImgRIPrivate *) &(pRasInfo->priv); + + if ((lockflags & (SD_LOCK_LUT)) != 0 && !bisdo->lutarray) { + /* REMIND: Should this be an InvalidPipe exception? */ + JNU_ThrowNullPointerException(env, "Attempt to lock missing colormap"); + return SD_FAILURE; + } +// TODO:BG + /* + if ((lockflags & SD_LOCK_INVCOLOR) != 0 || + (lockflags & SD_LOCK_INVGRAY) != 0) + { + bipriv->cData = BufImg_SetupICM(env, bisdo); + if (bipriv->cData == NULL) { + JNU_ThrowNullPointerException(env, "Could not initialize " + "inverse tables"); + return SD_FAILURE; + } + } else { + bipriv->cData = NULL; + } + */ + bipriv->cData = NULL; + + bipriv->lockFlags = lockflags; + bipriv->base = NULL; + bipriv->lutbase = NULL; + + SurfaceData_IntersectBounds(&pRasInfo->bounds, &bisdo->rasbounds); + + /* TODO:BG + if ((bipriv->lockFlags & SD_LOCK_WRITE) && + bisdo->sdOps.dirty != TRUE) { + SurfaceData_MarkDirty(env, &bisdo->sdOps); + } */ + return SD_SUCCESS; + } +} +static void ImageSD_Unlock(JNIEnv *env, SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo) +{ + ImageSDOps *isdo = (ImageSDOps*)ops; + + // For every ImageSD_Unlock, we need to be be conservative and mark the pixels + // as modified by the Sun2D renderer. + isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kNeedToSyncFromJavaPixelsIndex] = 1; + + pthread_mutex_unlock(&isdo->lock); +} +static void ImageSD_GetRasInfo(JNIEnv *env, SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo) +{ + // copied from BufImg_GetRasInfo in BufImgSurfaceData.c + { + BufImgSDOps *bisdo = (BufImgSDOps *)ops; + BufImgRIPrivate *bipriv = (BufImgRIPrivate *) &(pRasInfo->priv); + + if ((bipriv->lockFlags & (SD_LOCK_RD_WR)) != 0) { + bipriv->base = + (*env)->GetPrimitiveArrayCritical(env, bisdo->array, NULL); + } + if ((bipriv->lockFlags & (SD_LOCK_LUT)) != 0) { + bipriv->lutbase = + (*env)->GetPrimitiveArrayCritical(env, bisdo->lutarray, NULL); + } + + if (bipriv->base == NULL) { + pRasInfo->rasBase = NULL; + pRasInfo->pixelStride = 0; + pRasInfo->scanStride = 0; + } else { + pRasInfo->rasBase = (void *) + (((uintptr_t) bipriv->base) + bisdo->offset); + pRasInfo->pixelStride = bisdo->pixStr; + pRasInfo->scanStride = bisdo->scanStr; + } + if (bipriv->lutbase == NULL) { + pRasInfo->lutBase = NULL; + pRasInfo->lutSize = 0; + } else { + pRasInfo->lutBase = bipriv->lutbase; + pRasInfo->lutSize = bisdo->lutsize; + } + if (bipriv->cData == NULL) { + pRasInfo->invColorTable = NULL; + pRasInfo->redErrTable = NULL; + pRasInfo->grnErrTable = NULL; + pRasInfo->bluErrTable = NULL; + } else { + pRasInfo->invColorTable = bipriv->cData->img_clr_tbl; + pRasInfo->redErrTable = bipriv->cData->img_oda_red; + pRasInfo->grnErrTable = bipriv->cData->img_oda_green; + pRasInfo->bluErrTable = bipriv->cData->img_oda_blue; + pRasInfo->invGrayTable = bipriv->cData->pGrayInverseLutData; + } + } +} +static void ImageSD_Release(JNIEnv *env, SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo) +{ + // copied from BufImg_Release in BufImgSurfaceData.c + { + BufImgSDOps *bisdo = (BufImgSDOps *)ops; + BufImgRIPrivate *bipriv = (BufImgRIPrivate *) &(pRasInfo->priv); + + if (bipriv->base != NULL) { + jint mode = (((bipriv->lockFlags & (SD_LOCK_WRITE)) != 0) + ? 0 : JNI_ABORT); + (*env)->ReleasePrimitiveArrayCritical(env, bisdo->array, + bipriv->base, mode); + } + if (bipriv->lutbase != NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, bisdo->lutarray, + bipriv->lutbase, JNI_ABORT); + } + } +} + +JNIEXPORT void JNICALL Java_sun_java2d_OSXOffScreenSurfaceData_initRaster(JNIEnv *env, jobject bisd, jobject array, jint offset, jint width, jint height, + jint pixelStride, jint scanStride, jobject icm, jint type, + jobject jGraphicsState, jobjectArray jGraphicsStateObject, jobject jImageInfo) +{ +PRINT("Java_sun_java2d_OSXOffScreenSurfaceData_initRaster") + + ImageSDOps* isdo = (ImageSDOps*)SurfaceData_InitOps(env, bisd, sizeof(ImageSDOps)); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&isdo->lock, &attr); + pthread_mutex_lock(&isdo->lock); + pthread_mutexattr_destroy(&attr); + + // copied (and modified) from Java_sun_awt_image_BufImgSurfaceData_initRaster in BufImgSurfaceData.c + { + BufImgSDOps *bisdo = + //(BufImgSDOps*)SurfaceData_InitOps(env, bisd, sizeof(BufImgSDOps)); + (BufImgSDOps*)isdo; + //bisdo->sdOps.Lock = BufImg_Lock; + //bisdo->sdOps.GetRasInfo = BufImg_GetRasInfo; + //bisdo->sdOps.Release = BufImg_Release; + //bisdo->sdOps.Unlock = NULL; + //bisdo->sdOps.Dispose = BufImg_Dispose; + + bisdo->array = (*env)->NewWeakGlobalRef(env, array); + bisdo->offset = offset; + //bisdo->scanStr = scanStr; + bisdo->scanStr = scanStride; + //bisdo->pixStr = pixStr; + bisdo->pixStr = pixelStride; + if (!icm) { + bisdo->lutarray = NULL; + bisdo->lutsize = 0; + bisdo->icm = NULL; + } else { + jobject lutarray = (*env)->GetObjectField(env, icm, rgbID); + bisdo->lutarray = (*env)->NewWeakGlobalRef(env, lutarray); + bisdo->lutsize = (*env)->GetIntField(env, icm, mapSizeID); + bisdo->icm = (*env)->NewWeakGlobalRef(env, icm); + } + bisdo->rasbounds.x1 = 0; + bisdo->rasbounds.y1 = 0; + bisdo->rasbounds.x2 = width; + bisdo->rasbounds.y2 = height; + } + + isdo->nrOfPixelsOwners = 0; + + isdo->contextInfo = sDefaultContextInfo[type]; + isdo->imageInfo = sDefaultImageInfo[type]; + + isdo->contextInfo.bytesPerRow = width*isdo->contextInfo.bytesPerPixel; + isdo->imageInfo.bytesPerRow = width*isdo->imageInfo.bytesPerPixel; + + switch (type) + { + case java_awt_image_BufferedImage_TYPE_BYTE_GRAY: + isdo->contextInfo.colorSpace = isdo->imageInfo.colorSpace = gColorspaceGray; + break; + case java_awt_image_BufferedImage_TYPE_USHORT_GRAY: + isdo->contextInfo.colorSpace = gColorspaceRGB; + isdo->imageInfo.colorSpace = gColorspaceGray; + break; + default: + isdo->contextInfo.colorSpace = isdo->imageInfo.colorSpace = gColorspaceRGB; + break; + } + isdo->isSubImage = (offset%scanStride != 0) || (scanStride != (pixelStride*width)); + + // parameters specifying this image given to us from Java + isdo->javaImageInfo = (jint*)((*env)->GetDirectBufferAddress(env, jImageInfo)); + isdo->array = (array != NULL) ? JNFNewGlobalRef(env, array) : NULL; + isdo->offset = offset; + isdo->width = width; + isdo->height = height; + isdo->javaPixelBytes = pixelStride; + isdo->javaPixelsBytesPerRow = scanStride; + isdo->icm = (icm != NULL) ? JNFNewGlobalRef(env, icm) : NULL; + isdo->type = type; + + if ((isdo->javaImageInfo[sun_java2d_OSXOffScreenSurfaceData_kImageStolenIndex] == 1) || + (isdo->type == java_awt_image_BufferedImage_TYPE_CUSTOM)) + { + // don't waste (precious, precious) VRAM on stolen or custom images that will be slow no matter what + isdo->contextInfo.useWindowContextReference = NO; + } + + // needed by TYPE_BYTE_INDEXED + isdo->indexedColorTable = NULL; + isdo->lutData = NULL; + isdo->lutDataSize = 0; + if ((type == java_awt_image_BufferedImage_TYPE_BYTE_INDEXED) && ((*env)->IsSameObject(env, icm, NULL) == NO)) + { + jarray lutarray = JNFGetObjectField(env, icm, jm_rgb); + isdo->lutDataSize = (*env)->GetArrayLength(env, lutarray); + if (isdo->lutDataSize > 0) + { + jint transparency = JNFGetIntField(env, icm, jm_transparency); + jint transparent_index = -1; + if (transparency == java_awt_Transparency_BITMASK) + { + transparent_index = JNFGetIntField(env, icm, jm_transparent_index); + } + + Pixel32bit* lutdata = (Pixel32bit*)((*env)->GetPrimitiveArrayCritical(env, lutarray, NULL)); + if (lutdata != NULL) + { + isdo->lutData = NULL; + + isdo->lutData = malloc(isdo->lutDataSize * sizeof(Pixel32bit)); + if (isdo->lutData != NULL) + { + if (transparency == java_awt_Transparency_BITMASK) + { + Pixel32bit* src = lutdata; + Pixel32bit* dst = isdo->lutData; + jint i; + for (i=0; i<isdo->lutDataSize; i++) + { + if (i != transparent_index) + { + *dst = *src; + // rdar://problem/3390518 - don't force all indexed colors + // to be fully opaque. They could be set up for us. + // we used to call: *dst = 0xff000000 | *src; + // but that was forcing colors to be opaque when developers + // could have set the alpha. + } + else + { + *dst = 0x00000000; // mark as translucent color + } + dst++; src++; + } + } + else //if ((transparency == java_awt_Transparency_OPAQUE) || (transparency == java_awt_Transparency_TRANSLUCENT)) + { + jint mask = 0x00000000; + // <rdar://4224874> If the color model is OPAQUE than we need to create an opaque image for performance purposes. + // the default alphaInfo for INDEXED images is kCGImageAlphaFirst. Therefore we need to special case this. + if ((transparency == java_awt_Transparency_OPAQUE)) + { + isdo->imageInfo.alphaInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; + mask = 0xff000000; // this is just a safeguard to make sure we fill the alpha + } + + Pixel32bit* src = lutdata; + Pixel32bit* dst = isdo->lutData; + jint i; + for (i=0; i<isdo->lutDataSize; i++) + { + *dst = *src | mask; + dst++; src++; + } + } + + (*env)->ReleasePrimitiveArrayCritical(env, lutarray, lutdata, 0); + } + else + { + fprintf(stderr, "ERROR: malloc returns NULL for isdo->lutData in initRaster!\n"); + } + } + else + { + fprintf(stderr, "ERROR: GetPrimitiveArrayCritical returns NULL for lutdata in initRaster!\n"); + } + } + (*env)->DeleteLocalRef(env, lutarray); + } + + QuartzSDOps *qsdo = (QuartzSDOps*)isdo; + qsdo->BeginSurface = ImageSD_startCGContext; + qsdo->FinishSurface = ImageSD_finishCGContext; + + qsdo->javaGraphicsStates = (jint*)((*env)->GetDirectBufferAddress(env, jGraphicsState)); + qsdo->javaGraphicsStatesObjects = JNFNewGlobalRef(env, jGraphicsStateObject); + + qsdo->graphicsStateInfo.batchedLines = NULL; + qsdo->graphicsStateInfo.batchedLinesCount = 0; + + SurfaceDataOps *sdo = (SurfaceDataOps*)qsdo; + sdo->Lock = ImageSD_Lock; + sdo->Unlock = ImageSD_Unlock; + sdo->GetRasInfo = ImageSD_GetRasInfo; + sdo->Release = ImageSD_Release; + sdo->Setup = NULL; + sdo->Dispose = ImageSD_dispose; + + pthread_mutex_unlock(&isdo->lock); + +//PrintImageInfo(isdo); +} + +JNIEXPORT void JNICALL Java_sun_java2d_OSXOffScreenSurfaceData_initCustomRaster(JNIEnv* env, jobject bisd, jobject array, jint width, jint height, + jobject jGraphicsState, jobject jGraphicsStateObject, jobject jImageInfo) +{ +PRINT("Java_sun_java2d_OSXOffScreenSurfaceData_initCustomRaster") + jint offset = 0; + jint pixelStride = 4; + jint scanStride = pixelStride*width; + jobject icm = NULL; + jint type = java_awt_image_BufferedImage_TYPE_CUSTOM; + + Java_sun_java2d_OSXOffScreenSurfaceData_initRaster(env, bisd, array, offset, width, height, pixelStride, scanStride, icm, type, jGraphicsState, jGraphicsStateObject, jImageInfo); +} + +JNIEXPORT void JNICALL Java_sun_java2d_OSXOffScreenSurfaceData_syncToJavaPixels(JNIEnv *env, jobject bisd) +{ +PRINT("Java_sun_java2d_OSXOffScreenSurfaceData_syncToJavaPixels") + + syncToJavaPixels(env, (ImageSDOps*)SurfaceData_GetOps(env, bisd)); +} + +JNIEXPORT jboolean JNICALL Java_sun_java2d_OSXOffScreenSurfaceData_xorSurfacePixels + (JNIEnv *env, jobject dstIsd, jobject srcIsd, jint colorXOR, jint x, jint y, jint w, jint h) +{ +PRINT("Java_sun_java2d_OSXOffScreenSurfaceData_xorSurfacePixels") + return xorSurfacePixels(env, dstIsd, srcIsd, colorXOR, x, y, w, h); +} + +JNIEXPORT jboolean JNICALL Java_sun_java2d_OSXOffScreenSurfaceData_clearSurfacePixels + (JNIEnv *env, jobject bisd, jint w, jint h) +{ +PRINT("Java_sun_java2d_OSXOffScreenSurfaceData_clearSurfacePixels") + return clearSurfacePixels(env, bisd, w, h); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/PrintModel.h Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011, 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. + */ + + +#import <Foundation/Foundation.h> +#import <jni.h> + +@class NSPrintInfo; +@class NSView; + +@interface PrintModel : NSObject { + NSPrintInfo* fPrintInfo; +} + +- (id)initWithPrintInfo:(NSPrintInfo*)printInfo; +- (BOOL)runPageSetup; +- (BOOL)runJobSetup; +- (BOOL)runPrintLoopWithView:(NSView*)printerView waitUntilDone:(BOOL)wait withEnv:(JNIEnv *)env; +- (BOOL)safePrintLoop:(id)arg withEnv:(JNIEnv *)env; + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/PrintModel.m Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2011, 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. + */ + + +#import "PrintModel.h" + +#import <JavaNativeFoundation/JavaNativeFoundation.h> + +#import "PrinterView.h" +#import "ThreadUtilities.h" + +@implementation PrintModel + +- (id)initWithPrintInfo:(NSPrintInfo*)printInfo { + self = [super init]; + if (self) { + fPrintInfo = [printInfo retain]; + } + + return self; +} + +- (void)dealloc { + [fPrintInfo release]; + fPrintInfo = nil; + + [super dealloc]; +} +//- (void)finalize { [super finalize]; } + +- (BOOL)runPageSetup { + __block BOOL fResult = NO; + + [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + NSPageLayout* pageLayout = [NSPageLayout pageLayout]; + fResult = ([pageLayout runModalWithPrintInfo:fPrintInfo] == NSOKButton); + }]; + + return fResult; +} + +- (BOOL)runJobSetup { + __block BOOL fResult = NO; + + [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + NSPrintPanel* printPanel = [NSPrintPanel printPanel]; + fResult = ([printPanel runModalWithPrintInfo:fPrintInfo] == NSOKButton); + }]; + + return fResult; +} + +- (BOOL)runPrintLoopWithView:(NSView*)printerView waitUntilDone:(BOOL)wait withEnv:(JNIEnv *)env +{ +AWT_ASSERT_NOT_APPKIT_THREAD; + + BOOL fResult = NO; + + // <rdar://problem/4310184> Because people like to put up modal dialogs during print operations, + // we have to run the print operation on a non-AppKit thread or else we get a deadlock and errors + // the AppKit team believes it's OK for us to call runOperation from non-AppKit threads, + // as long as we don't show any panels, and we don't touch the NSPrintInfo or the NSView from other threads. + if (wait) { + fResult = [self safePrintLoop:printerView withEnv:env]; + } else { + // Retain these so they don't go away while we're in Java + CFRetain(self); // GC + if (printerView) CFRetain(printerView); // GC + + static JNF_CLASS_CACHE(jc_CPrinterJob, "sun/lwawt/macosx/CPrinterJob"); + static JNF_STATIC_MEMBER_CACHE(jm_detachPrintLoop, jc_CPrinterJob, "detachPrintLoop", "(JJ)V"); + JNFCallStaticVoidMethod(env, jm_detachPrintLoop, ptr_to_jlong(self), ptr_to_jlong(printerView)); // AWT_THREADING Safe (known object) + } + + return fResult; +} + +- (BOOL) safePrintLoop:(id)arg withEnv:(JNIEnv *)env +{ +AWT_ASSERT_NOT_APPKIT_THREAD; + + PrinterView* printerView = (PrinterView*)arg; + BOOL fResult; + @try { + NSPrintOperation* printLoop = [NSPrintOperation printOperationWithView:printerView printInfo:fPrintInfo]; + [printLoop setShowPanels:NO]; //+++gdb Problem: This will avoid progress bars... + //[printLoop setCanSpawnSeparateThread:YES]; //+++gdb Need to check this... + + fResult = [printLoop runOperation]; + } @finally { + // Tell CPrinterJob that things are done. + [printerView complete:env]; + } + return fResult; +} + +@end + +/* + * Class: sun_lwawt_macosx_CPrinterJob + * Method: _safePrintLoop + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPrinterJob__1safePrintLoop +(JNIEnv *env, jclass clz, jlong target, jlong view) +{ +JNF_COCOA_ENTER(env); + + PrintModel *model = (PrintModel *)jlong_to_ptr(target); + PrinterView *arg = (PrinterView *)jlong_to_ptr(view); + + [model safePrintLoop:arg withEnv:env]; + + // These are to match the retains in runPrintLoopWithView: + if (model) CFRelease(model); // GC + if (arg) CFRelease(arg); // GC + +JNF_COCOA_EXIT(env); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/PrinterSurfaceData.h Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011, 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. + */ + +#import "QuartzSurfaceData.h" + +struct _PrintSDOps +{ + QuartzSDOps qsdo; // must be the first entry! + + NSGraphicsContext *nsRef; + + jint width; + jint height; +}; +typedef struct _PrintSDOps PrintSDOps;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/PrinterSurfaceData.m Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2011, 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. + */ + + +#import "PrinterSurfaceData.h" +#import <JavaNativeFoundation/JavaNativeFoundation.h> + + +//#define DEBUG 1 +#if defined DEBUG + #define PRINT(msg) {fprintf(stderr, "%s\n", msg);} +#else + #define PRINT(msg) {} +#endif + +static LockFunc PrintSD_Lock; +static UnlockFunc PrintSD_Unlock; +static GetRasInfoFunc PrintSD_GetRasInfo; +static ReleaseFunc PrintSD_ReleaseRasInfo; +static void flush(JNIEnv *env, QuartzSDOps *qsdo); + +static void PrintSD_startCGContext(JNIEnv *env, QuartzSDOps *qsdo, SDRenderType renderType) +{ +PRINT(" PrintSD_startCGContext") + + if (qsdo->cgRef != NULL) + { + flush(env, qsdo); + + SetUpCGContext(env, qsdo, renderType); + } +} + +static void PrintSD_finishCGContext(JNIEnv *env, QuartzSDOps *qsdo) +{ +PRINT(" PrintSD_finishCGContext") + + if (qsdo->cgRef != NULL) + { + CompleteCGContext(env, qsdo); + } +} + +static void PrintSD_dispose(JNIEnv *env, SurfaceDataOps *sdo) +{ +PRINT(" PrintSD_dispose") + QuartzSDOps *qsdo = (QuartzSDOps *)sdo; + + (*env)->DeleteGlobalRef(env, qsdo->javaGraphicsStatesObjects); + + if (qsdo->graphicsStateInfo.batchedLines != NULL) + { + free(qsdo->graphicsStateInfo.batchedLines); + qsdo->graphicsStateInfo.batchedLines = NULL; + } + + qsdo->BeginSurface = NULL; + qsdo->FinishSurface = NULL; +} + +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPrinterSurfaceData_initOps(JNIEnv *env, jobject jthis, jlong nsRef, jobject jGraphicsState, jobjectArray jGraphicsStateObject, jint width, jint height) +{ +JNF_COCOA_ENTER(env); + +PRINT("Java_sun_lwawt_macosx_CPrinterSurfaceData_initOps") + + PrintSDOps *psdo = (PrintSDOps*)SurfaceData_InitOps(env, jthis, sizeof(PrintSDOps)); + psdo->nsRef = (NSGraphicsContext*)jlong_to_ptr(nsRef); + psdo->width = width; + psdo->height = height; + + QuartzSDOps *qsdo = (QuartzSDOps*)psdo; + qsdo->BeginSurface = PrintSD_startCGContext; + qsdo->FinishSurface = PrintSD_finishCGContext; + qsdo->cgRef = [psdo->nsRef graphicsPort]; + + qsdo->javaGraphicsStates = (jint*)((*env)->GetDirectBufferAddress(env, jGraphicsState)); + qsdo->javaGraphicsStatesObjects = (*env)->NewGlobalRef(env, jGraphicsStateObject); + + qsdo->graphicsStateInfo.batchedLines = NULL; + qsdo->graphicsStateInfo.batchedLinesCount = 0; + + SurfaceDataOps *sdo = (SurfaceDataOps*)qsdo; + sdo->Lock = PrintSD_Lock; + sdo->Unlock = PrintSD_Unlock; + sdo->GetRasInfo = PrintSD_GetRasInfo; + sdo->Release = PrintSD_ReleaseRasInfo; + sdo->Setup = NULL; + sdo->Dispose = PrintSD_dispose; + +JNF_COCOA_EXIT(env); +} + +static jint PrintSD_Lock(JNIEnv *env, SurfaceDataOps *sdo, SurfaceDataRasInfo *pRasInfo, jint lockflags) +{ +PRINT(" PrintSD_Lock") + jint status = SD_FAILURE; + + //QuartzSDOps *qsdo = (QuartzSDOps*)sdo; + //PrintSD_startCGContext(env, qsdo, SD_Image); + + status = SD_SUCCESS; + + return status; +} +static void PrintSD_Unlock(JNIEnv *env, SurfaceDataOps *sdo, SurfaceDataRasInfo *pRasInfo) +{ +PRINT(" PrintSD_Unlock") + + //QuartzSDOps *qsdo = (QuartzSDOps*)sdo; + //PrintSD_finishCGContext(env, qsdo); +} +static void PrintSD_GetRasInfo(JNIEnv *env, SurfaceDataOps *sdo, SurfaceDataRasInfo *pRasInfo) +{ +PRINT(" PrintSD_GetRasInfo") + PrintSDOps *psdo = (PrintSDOps*)sdo; + + pRasInfo->pixelStride = 4; // ARGB + pRasInfo->scanStride = psdo->width * pRasInfo->pixelStride; + + pRasInfo->rasBase = NULL; //psdo->dataForSun2D; +} +static void PrintSD_ReleaseRasInfo(JNIEnv *env, SurfaceDataOps *sdo, SurfaceDataRasInfo *pRasInfo) +{ +PRINT(" PrintSD_ReleaseRasInfo") + + pRasInfo->pixelStride = 0; + pRasInfo->scanStride = 0; + pRasInfo->rasBase = NULL; +} + +static void dataProvider_FreeSun2DPixels(void *info, const void *data, size_t size) +{ +PRINT("dataProvider_FreeSun2DPixels") + // CGBitmapFreeData(info); + free(info); +} +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPrinterSurfaceData__1flush + (JNIEnv *env, jobject jsurfacedata) +{ + flush(env, (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata)); +} +static void flush(JNIEnv *env, QuartzSDOps *qsdo) +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/PrinterView.h Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011, 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. + */ + +#import <Cocoa/Cocoa.h> +#import <jni.h> + +@interface PrinterView : NSView { + jobject fPrinterJob; // CPrinterJob + jobject fCurPageFormat; + jobject fCurPainter; + jobject fCurPeekGraphics; + + jint fFirstPage, fLastPage; +} + +- (id)initWithFrame:(NSRect)aRect withEnv:(JNIEnv*)env withPrinterJob:(jobject)printerJob; + +- (void)setFirstPage:(jint)firstPage lastPage:(jint)lastPage; + +- (void)releaseReferences:(JNIEnv*)env; + +- (void)drawRect:(NSRect)aRect; + +- (NSString*)printJobTitle; +- (BOOL)knowsPageRange:(NSRangePointer)aRange; +- (NSRect)rectForPage:(NSInteger)pageNumber; + +- (BOOL)cancelCheck:(JNIEnv*)env; + +- (void)complete:(JNIEnv*)env; + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/PrinterView.m Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2011, 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. + */ + +#import "PrinterView.h" + +#import "java_awt_print_Pageable.h" +#import "java_awt_print_Printable.h" + +#import <JavaNativeFoundation/JavaNativeFoundation.h> + +#import "ThreadUtilities.h" +#import "GeomUtilities.h" + + +static JNF_CLASS_CACHE(sjc_CPrinterJob, "sun/lwawt/macosx/CPrinterJob"); + +@implementation PrinterView + +- (id)initWithFrame:(NSRect)aRect withEnv:(JNIEnv*)env withPrinterJob:(jobject)printerJob +{ + self = [super initWithFrame:aRect]; + if (self) + { + fPrinterJob = JNFNewGlobalRef(env, printerJob); + fCurPageFormat = NULL; + fCurPainter = NULL; + fCurPeekGraphics = NULL; + } + return self; +} + +- (void)releaseReferences:(JNIEnv*)env +{ + if (fCurPageFormat != NULL) + { + JNFDeleteGlobalRef(env, fCurPageFormat); + fCurPageFormat = NULL; + } + if (fCurPainter != NULL) + { + JNFDeleteGlobalRef(env, fCurPainter); + fCurPainter = NULL; + } + if (fCurPeekGraphics != NULL) + { + JNFDeleteGlobalRef(env, fCurPeekGraphics); + fCurPeekGraphics = NULL; + } +} + +- (void)setFirstPage:(jint)firstPage lastPage:(jint)lastPage { + fFirstPage = firstPage; + fLastPage = lastPage; +} + +- (void)drawRect:(NSRect)aRect +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_MEMBER_CACHE(jm_printToPathGraphics, sjc_CPrinterJob, "printToPathGraphics", "(Lsun/print/PeekGraphics;Ljava/awt/print/PrinterJob;Ljava/awt/print/Printable;Ljava/awt/print/PageFormat;IJ)V"); + + // Create and draw into a new CPrinterGraphics with the current Context. + assert(fCurPageFormat != NULL); + assert(fCurPainter != NULL); + assert(fCurPeekGraphics != NULL); + + JNIEnv* env = [ThreadUtilities getJNIEnvUncached]; + + if ([self cancelCheck:env]) + { + [self releaseReferences:env]; + return; + } + + NSPrintOperation* printLoop = [NSPrintOperation currentOperation]; + jint jPageIndex = [printLoop currentPage] - 1; + + jlong context = ptr_to_jlong([printLoop context]); + CGContextRef cgRef = (CGContextRef)[[printLoop context] graphicsPort]; + CGContextSaveGState(cgRef); //04/28/2004: state needs to be saved here due to addition of lazy state management + + JNFCallVoidMethod(env, fPrinterJob, jm_printToPathGraphics, fCurPeekGraphics, fPrinterJob, fCurPainter, fCurPageFormat, jPageIndex, context); // AWT_THREADING Safe (AWTRunLoop) + + CGContextRestoreGState(cgRef); + + [self releaseReferences:env]; +} + +- (NSString*)printJobTitle +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_MEMBER_CACHE(jm_getJobName, sjc_CPrinterJob, "getJobName", "()Ljava/lang/String;"); + + JNIEnv* env = [ThreadUtilities getJNIEnvUncached]; + + jobject o = JNFCallObjectMethod(env, fPrinterJob, jm_getJobName); // AWT_THREADING Safe (known object) + id result = JNFJavaToNSString(env, o); + (*env)->DeleteLocalRef(env, o); + return result; +} + +- (BOOL)knowsPageRange:(NSRangePointer)aRange +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + JNIEnv* env = [ThreadUtilities getJNIEnvUncached]; + if ([self cancelCheck:env]) + { + return NO; + } + + aRange->location = fFirstPage + 1; + + if (fLastPage == java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES) + { + aRange->length = NSIntegerMax; + } + else + { + aRange->length = (fLastPage + 1) - fFirstPage; + } + + return YES; +} + +- (NSRect)rectForPage:(NSInteger)pageNumber +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_MEMBER_CACHE(jm_getPageformatPrintablePeekgraphics, sjc_CPrinterJob, "getPageformatPrintablePeekgraphics", "(I)[Ljava/lang/Object;"); + static JNF_MEMBER_CACHE(jm_printAndGetPageFormatArea, sjc_CPrinterJob, "printAndGetPageFormatArea", "(Ljava/awt/print/Printable;Ljava/awt/Graphics;Ljava/awt/print/PageFormat;I)Ljava/awt/geom/Rectangle2D;"); + + // Assertions removed, and corresponding JNFDeleteGlobalRefs added, for radr://3962543 + // Actual fix that will keep these assertions from being true is radr://3205462 , + // which will hopefully be fixed by the blocking AppKit bug radr://3056694 + //assert(fCurPageFormat == NULL); + //assert(fCurPainter == NULL); + //assert(fCurPeekGraphics == NULL); + + JNIEnv* env = [ThreadUtilities getJNIEnvUncached]; + if(fCurPageFormat != NULL) { + JNFDeleteGlobalRef(env, fCurPageFormat); + } + if(fCurPainter != NULL) { + JNFDeleteGlobalRef(env, fCurPainter); + } + if(fCurPeekGraphics != NULL) { + JNFDeleteGlobalRef(env, fCurPeekGraphics); + } + + //+++gdb Check the pageNumber for validity (PageAttrs) + + jint jPageNumber = pageNumber - 1; + + NSRect result; + + if ([self cancelCheck:env]) + { + return NSZeroRect; + } + + jobjectArray objectArray = JNFCallObjectMethod(env, fPrinterJob, jm_getPageformatPrintablePeekgraphics, jPageNumber); // AWT_THREADING Safe (AWTRunLoopMode) + if (objectArray != NULL) { + // Get references to the return objects -> PageFormat, Printable, PeekGraphics + // Cheat - we know we either got NULL or a 3 element array + jobject pageFormat = (*env)->GetObjectArrayElement(env, objectArray, 0); + fCurPageFormat = JNFNewGlobalRef(env, pageFormat); + (*env)->DeleteLocalRef(env, pageFormat); + + jobject painter = (*env)->GetObjectArrayElement(env, objectArray, 1); + fCurPainter = JNFNewGlobalRef(env, painter); + (*env)->DeleteLocalRef(env, painter); + + jobject peekGraphics = (*env)->GetObjectArrayElement(env, objectArray, 2); + fCurPeekGraphics = JNFNewGlobalRef(env, peekGraphics); + (*env)->DeleteLocalRef(env, peekGraphics); + + // Actually print and get the PageFormatArea + jobject pageFormatArea = JNFCallObjectMethod(env, fPrinterJob, jm_printAndGetPageFormatArea, fCurPainter, fCurPeekGraphics, fCurPageFormat, jPageNumber); // AWT_THREADING Safe (AWTRunLoopMode) + if (pageFormatArea != NULL) { + result = JavaToNSRect(env, pageFormatArea); + (*env)->DeleteLocalRef(env, pageFormatArea); + } else { + [self releaseReferences:env]; + result = NSZeroRect; + } + + (*env)->DeleteLocalRef(env, objectArray); + } else { + [self releaseReferences:env]; + result = NSZeroRect; + } + + return result; +} + +- (BOOL)cancelCheck:(JNIEnv*)env +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_MEMBER_CACHE(jm_cancelCheck, sjc_CPrinterJob, "cancelCheck", "()Z"); + + return JNFCallBooleanMethod(env, fPrinterJob, jm_cancelCheck); // AWT_THREADING Safe (known object) +} + +// This is called by -[PrintModel safePrintLoop] +- (void)complete:(JNIEnv*)env +{ + AWT_ASSERT_NOT_APPKIT_THREAD; + + static JNF_MEMBER_CACHE(jf_completePrintLoop, sjc_CPrinterJob, "completePrintLoop", "()V"); + JNFCallVoidMethod(env, fPrinterJob, jf_completePrintLoop); + + // Clean up after ourselves + // Can't put these into -dealloc since that happens (potentially) after the JNIEnv is stale + [self releaseReferences:env]; + if (fPrinterJob != NULL) + { + JNFDeleteGlobalRef(env, fPrinterJob); + fPrinterJob = NULL; + } +} + +- (BOOL)isFlipped +{ + return TRUE; +} + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macosx/native/sun/awt/QuartzRenderer.m Fri Sep 23 13:42:06 2011 -0700 @@ -0,0 +1,794 @@ +/* + * Copyright (c) 2011, 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. + */ + +#import "java_awt_image_BufferedImage.h" +#import "java_awt_geom_PathIterator.h" +#import "sun_java2d_OSXSurfaceData.h" + +#import <stdio.h> +#import <JavaNativeFoundation/JavaNativeFoundation.h> + +#import "ImageSurfaceData.h" + + +//#define DEBUG 1 +#if defined DEBUG + #define QUARTZ_RENDERER_INLINE + #define PRINT(msg) {fprintf(stderr, "%s\n", msg);fflush(stderr);} +#else + #define QUARTZ_RENDERER_INLINE static inline + #define PRINT(msg) {} +#endif + +// Copied the following from Math.java +#define PI 3.14159265358979323846f + +#define BATCHED_POINTS_SIZE 1024 + +// same value as defined in Sun's own code +#define XOR_ALPHA_CUTOFF 128 + +// private Quartz routines needed here +CG_EXTERN void CGContextSetCTM(CGContextRef ref, CGAffineTransform tx); + + +static CGFloat gRoundRectCtrlpts[10][12] = +{ + {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.0f, 0.0f}, + {1.0f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, -0.5f}, + {1.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, 0.0f, 0.0f}, + {0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, +}; + +CG_EXTERN CGRect CGRectApplyAffineTransform(CGRect rect, CGAffineTransform t); + + +CGRect sanitizedRect(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2) { + CGFloat temp; + if (x1 > x2) { + temp = x2; + x2 = x1; + x1 = temp; + } + if (y1 > y2) { + temp = y2; + y2 = y1; + y1 = temp; + } + return CGRectMake(x1, y1, x2-x1, y2-y1); +} + +QUARTZ_RENDERER_INLINE SDRenderType doLineUsingCG(CGContextRef cgRef, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, BOOL simple, CGFloat offsetX, CGFloat offsetY) +{ +//fprintf(stderr, "doLine start=(%f, %f), end=(%f, %f), linewidth:%f, offsetX:%f, offsetY:%f\n", x1, y1, x2, y2, CGContextGetLineWidth(cgRef), offsetX, offsetY); + SDRenderType renderType = SD_Nothing; + + if (simple == YES) + { + struct CGPoint oneLinePoints[2]; + + oneLinePoints[0] = CGPointMake(x1+offsetX, y1+offsetY); + oneLinePoints[1] = CGPointMake(x2+offsetX, y2+offsetY); + + CGContextStrokeLineSegments(cgRef, oneLinePoints, 2); + renderType = SD_Nothing; + } + else + { + CGContextMoveToPoint(cgRef, x1+offsetX, y1+offsetY); + CGContextAddLineToPoint(cgRef, x2+offsetX, y2+offsetY); + renderType = SD_Stroke; + } + + return renderType; +} +QUARTZ_RENDERER_INLINE SDRenderType doLine(QuartzSDOps *qsdo, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2) +{ +PRINT(" doLine") + if (YES) + { + return doLineUsingCG(qsdo->cgRef, x1, y1, x2, y2, + qsdo->graphicsStateInfo.simpleStroke, qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY); + } + // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.) +} + + +QUARTZ_RENDERER_INLINE SDRenderType doRectUsingCG(CGContextRef cgRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h, BOOL fill, BOOL simple, CGFloat offsetX, CGFloat offsetY) +{ +//fprintf(stderr, "doRect point=(%f, %f), size=(%f, %f), offsets=(%f, %f) fill=%d simple=%d\n", x, y, w, h, offsetX, offsetY, fill, simple); +//CGRect clip = CGContextGetClipBoundingBox(cgRef); +//fprintf(stderr, " clip: ((%f, %f), (%f, %f))\n", clip.origin.x, clip.origin.y, clip.size.width, clip.size.height); +//CGAffineTransform ctm = CGContextGetCTM(cgRef); +//fprintf(stderr, " ctm: (%f, %f, %f, %f, %f, %f)\n", ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty); + SDRenderType renderType = SD_Nothing; + + if (fill == YES) + { + if (simple == YES) + { + CGContextFillRect(cgRef, CGRectMake(x, y, w, h)); + renderType = SD_Nothing; + } + else + { + CGContextAddRect(cgRef, CGRectMake(x, y, w, h)); + renderType = SD_Fill; + } + } + else + { + if (simple == YES) + { + CGContextStrokeRect(cgRef, CGRectMake(x+offsetX, y+offsetY, w, h)); + renderType = SD_Nothing; + } + else + { + CGContextAddRect(cgRef, CGRectMake(x+offsetX, y+offsetY, w, h)); + renderType = SD_Stroke; + } + } + + return renderType; +} +QUARTZ_RENDERER_INLINE SDRenderType doRect(QuartzSDOps *qsdo, CGFloat x, CGFloat y, CGFloat w, CGFloat h, BOOL fill) +{ +PRINT(" doRect") + if (YES) + { + return doRectUsingCG(qsdo->cgRef, x, y, w, h, fill, + qsdo->graphicsStateInfo.simpleStroke, qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY); + } + // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.) +} + +// from RoundRectIterator.java +QUARTZ_RENDERER_INLINE SDRenderType doRoundRectUsingCG(CGContextRef cgRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h, CGFloat arcWidth, CGFloat arcHeight, BOOL fill, CGFloat offsetX, CGFloat offsetY) +{ + SDRenderType renderType = SD_Nothing; + + if (fill == YES) + { + renderType = SD_Fill; + } + else + { + renderType = SD_Stroke; + } + + // radr://3593731 RoundRects with corner width/height of 0 don't draw + arcWidth = (arcWidth > 0.0f) ? arcWidth : 0.0f; + arcHeight = (arcHeight > 0.0f) ? arcHeight : 0.0f; + + CGFloat aw = (w < arcWidth) ? w : arcWidth; + CGFloat ah = (h < arcHeight) ? h : arcHeight; + + CGFloat *ctrls, p1, q1, p2, q2, p3, q3; + ctrls = gRoundRectCtrlpts[0]; + p1 = (x + ctrls[0] * w + ctrls[1] * aw); + q1 = (y + ctrls[2] * h + ctrls[3] * ah); + CGContextMoveToPoint(cgRef, p1+offsetX, q1+offsetY); + + ctrls = gRoundRectCtrlpts[1]; + p1 = (x + ctrls[0] * w + ctrls[1] * aw); + q1 = (y + ctrls[2] * h + ctrls[3] * ah); + CGContextAddLineToPoint(cgRef, p1+offsetX, q1+offsetY); + + ctrls = gRoundRectCtrlp