From a15c7805028e44c0638c1b06ffe964bc850ef000 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 7 Mar 2008 12:13:17 -0800 Subject: [PATCH 01/40] 6640532: Graphics.getFontMetrics() throws NullPointerException NIO usage needs to be robust against Thread.interrupt() Reviewed-by: tdv --- .../share/classes/sun/font/FontManager.java | 56 +++++++----- .../java/awt/font/Threads/FontThread.java | 89 +++++++++++++++++++ 2 files changed, 124 insertions(+), 21 deletions(-) create mode 100644 jdk/test/java/awt/font/Threads/FontThread.java diff --git a/jdk/src/share/classes/sun/font/FontManager.java b/jdk/src/share/classes/sun/font/FontManager.java index 820c72728d3..be059a725d9 100644 --- a/jdk/src/share/classes/sun/font/FontManager.java +++ b/jdk/src/share/classes/sun/font/FontManager.java @@ -93,7 +93,6 @@ public final class FontManager { */ private static final int CHANNELPOOLSIZE = 20; private static int lastPoolIndex = 0; - private static int poolSize = 0; private static FileFont fontFileCache[] = new FileFont[CHANNELPOOLSIZE]; /* Need to implement a simple linked list scheme for fast @@ -283,29 +282,32 @@ public final class FontManager { private static native void initIDs(); public static void addToPool(FileFont font) { - boolean added = false; + + FileFont fontFileToClose = null; + int freeSlot = -1; + synchronized (fontFileCache) { - /* use poolSize to quickly detect if there's any free slots. - * This is a performance tweak based on the assumption that - * if this is executed at all often, its because there are many - * fonts being used and the pool will be full, and we will save - * a fruitless iteration + /* Avoid duplicate entries in the pool, and don't close() it, + * since this method is called only from within open(). + * Seeing a duplicate is most likely to happen if the thread + * was interrupted during a read, forcing perhaps repeated + * close and open calls and it eventually it ends up pointing + * at the same slot. */ - if (poolSize < CHANNELPOOLSIZE) { - for (int i=0; i= 0) { + fontFileCache[freeSlot] = font; + return; } else { - // is it possible for this to be the same font? - assert fontFileCache[lastPoolIndex] != font; - /* replace with new font, poolSize is unchanged. */ - fontFileCache[lastPoolIndex].close(); + /* replace with new font. */ + fontFileToClose = fontFileCache[lastPoolIndex]; fontFileCache[lastPoolIndex] = font; /* lastPoolIndex is updated so that the least recently opened * file will be closed next. @@ -313,6 +315,19 @@ public final class FontManager { lastPoolIndex = (lastPoolIndex+1) % CHANNELPOOLSIZE; } } + /* Need to close the font file outside of the synchronized block, + * since its possible some other thread is in an open() call on + * this font file, and could be holding its lock and the pool lock. + * Releasing the pool lock allows that thread to continue, so it can + * then release the lock on this font, allowing the close() call + * below to proceed. + * Also, calling close() is safe because any other thread using + * the font we are closing() synchronizes all reading, so we + * will not close the file while its in use. + */ + if (fontFileToClose != null) { + fontFileToClose.close(); + } } /* @@ -334,7 +349,6 @@ public final class FontManager { for (int i=0; i Date: Wed, 9 Apr 2008 13:11:46 -0700 Subject: [PATCH 02/40] 6683472: Incorrect handling of translation component of font transform Reviewed-by: igor, campbell --- .../classes/sun/font/AttributeValues.java | 4 +- .../Graphics2D/DrawString/RotTransText.java | 88 +++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java diff --git a/jdk/src/share/classes/sun/font/AttributeValues.java b/jdk/src/share/classes/sun/font/AttributeValues.java index bc1b74e951a..72ea0664c35 100644 --- a/jdk/src/share/classes/sun/font/AttributeValues.java +++ b/jdk/src/share/classes/sun/font/AttributeValues.java @@ -887,10 +887,10 @@ public final class AttributeValues implements Cloneable { try { AffineTransform rtxi = rtx.createInverse(); + double dx = tx.getTranslateX(); + double dy = tx.getTranslateY(); tx.preConcatenate(rtxi); if (andTranslation) { - double dx = tx.getTranslateX(); - double dy = tx.getTranslateY(); if (dx != 0 || dy != 0) { tx.setTransform(tx.getScaleX(), tx.getShearY(), tx.getShearX(), tx.getScaleY(), 0, 0); diff --git a/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java b/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java new file mode 100644 index 00000000000..e130b1c4ae4 --- /dev/null +++ b/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java @@ -0,0 +1,88 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 6683472 + * @summary Transformed fonts using drawString and TextLayout should be in + * the same position. + */ + +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.util.HashMap; + +public class RotTransText { + + public static void main(String[] args) { + + int wid=400, hgt=400; + BufferedImage bi = + new BufferedImage(wid, hgt, BufferedImage.TYPE_INT_RGB); + + Graphics2D g2d = bi.createGraphics(); + + g2d.setColor(Color.white); + g2d.fillRect(0, 0, wid, hgt); + + int x=130, y=130; + String s = "Text"; + + int xt=90, yt=50; + for (int angle=0;angle<360;angle+=30) { + AffineTransform aff = AffineTransform.getTranslateInstance(50,90); + aff.rotate(angle * Math.PI/180.0); + + Font fnt = new Font("SansSerif", Font.PLAIN, 60); + fnt = fnt.deriveFont(Font.PLAIN, aff); + g2d.setFont(fnt); + g2d.setColor(Color.blue); + g2d.drawString(s, x, y); + + g2d.setColor(Color.red); + FontRenderContext frc = g2d.getFontRenderContext(); + HashMap attrMap = new HashMap(); + attrMap.put(TextAttribute.STRIKETHROUGH, + TextAttribute.STRIKETHROUGH_ON); + fnt = fnt.deriveFont(attrMap); + TextLayout tl = new TextLayout(s, fnt, frc); + tl.draw(g2d, (float)x, (float)y); + } + // Test BI: should be no blue: only red and white. + int red = Color.red.getRGB(); + int blue = Color.blue.getRGB(); + int white = Color.white.getRGB(); + for (int px=0;px 0)) { - OrientationRequested[] orientSup = + orientSup = new OrientationRequested[orientArray.length]; for (int i=0; i= AttributeClass.TAG_INT && + while (response[0] >= AttributeClass.TAG_UNSUPPORTED_VALUE && response[0] <= AttributeClass.TAG_MEMBER_ATTRNAME) { // read name length len = ois.readShort(); @@ -1710,12 +1736,16 @@ public class IPPPrintService implements PrintService, SunPrinterJobService { respList.add(responseMap); responseMap = new HashMap(); } - AttributeClass ac = - new AttributeClass(attribStr, - valTagByte, - outArray); - responseMap.put(ac.getName(), ac); + // exclude those that are unknown + if (valTagByte >= AttributeClass.TAG_INT) { + AttributeClass ac = + new AttributeClass(attribStr, + valTagByte, + outArray); + + responseMap.put(ac.getName(), ac); + } outObj = new ByteArrayOutputStream(); counter = 0; //reset counter From b3d5835158821b07059c4650a7fa5fcbc3c73ac8 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 10 Apr 2008 09:05:01 -0700 Subject: [PATCH 04/40] 6684056: SUPERSCRIPT TextAttribute on font needs to trigger layout Reviewed-by: igor, campbell --- jdk/src/share/classes/java/awt/Font.java | 2 +- .../Graphics2D/DrawString/DrawStrSuper.java | 151 ++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 jdk/test/java/awt/Graphics2D/DrawString/DrawStrSuper.java diff --git a/jdk/src/share/classes/java/awt/Font.java b/jdk/src/share/classes/java/awt/Font.java index 92be954a273..ba2cf3e3976 100644 --- a/jdk/src/share/classes/java/awt/Font.java +++ b/jdk/src/share/classes/java/awt/Font.java @@ -711,7 +711,7 @@ public class Font implements java.io.Serializable EBIDI_EMBEDDING, EJUSTIFICATION, EINPUT_METHOD_HIGHLIGHT, EINPUT_METHOD_UNDERLINE, ESWAP_COLORS, ENUMERIC_SHAPING, EKERNING, - ELIGATURES, ETRACKING); + ELIGATURES, ETRACKING, ESUPERSCRIPT); private static final int EXTRA_MASK = AttributeValues.getMask(ETRANSFORM, ESUPERSCRIPT, EWIDTH); diff --git a/jdk/test/java/awt/Graphics2D/DrawString/DrawStrSuper.java b/jdk/test/java/awt/Graphics2D/DrawString/DrawStrSuper.java new file mode 100644 index 00000000000..2a31b2da7ea --- /dev/null +++ b/jdk/test/java/awt/Graphics2D/DrawString/DrawStrSuper.java @@ -0,0 +1,151 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 6684056 + * @summary Super-scripted text needs to be positioned the same with + * drawString and TextLayout. + */ +import java.awt.*; +import java.awt.event.*; +import java.awt.font.*; +import static java.awt.font.TextAttribute.*; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.util.HashMap; + + +public class DrawStrSuper extends Component { + + int angle = 0; + static boolean interactive = false; + + int wid=400, hgt=400; + BufferedImage bi = null; + + void paintImage() { + + if (bi == null) { + bi = new BufferedImage(wid, hgt, BufferedImage.TYPE_INT_RGB); + } + Graphics2D g2d = bi.createGraphics(); + g2d.setColor(Color.white); + g2d.fillRect(0, 0, wid, hgt); + g2d.translate(200, 200); + + Font fnt = new Font("Arial", Font.PLAIN, 20); + fnt = fnt.deriveFont(60.0f); + HashMap attrMap = new HashMap(); + AffineTransform aff = + AffineTransform.getRotateInstance(angle * Math.PI/180.0); + attrMap.put(SUPERSCRIPT, SUPERSCRIPT_SUPER); + attrMap.put(TRANSFORM, aff); + fnt = fnt.deriveFont(attrMap); + + g2d.setFont(fnt); + g2d.setColor(Color.yellow); + TextLayout tl = new TextLayout("Text", fnt,g2d.getFontRenderContext()); + g2d.fill(tl.getBounds()); + + g2d.setColor(Color.black); + g2d.drawLine(-3, 0, 3, 0); + g2d.drawLine(0, -3, 0, 3); + + g2d.setColor(Color.blue); + g2d.drawString("Text", 0, 0); + + g2d.setColor(Color.red); + tl.draw(g2d,0f,0f); + + // Test BI: should be no blue + int blue = Color.blue.getRGB(); + for (int px=0;px Date: Thu, 10 Apr 2008 10:31:19 -0700 Subject: [PATCH 05/40] 6638477: Two external URLS referenced in 2D documentation are no longer functioning Reviewed-by: jgodinez --- jdk/src/share/classes/java/awt/font/OpenType.java | 6 +++--- .../attribute/standard/ReferenceUriSchemesSupported.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jdk/src/share/classes/java/awt/font/OpenType.java b/jdk/src/share/classes/java/awt/font/OpenType.java index c83b480f0df..52009d3c7e6 100644 --- a/jdk/src/share/classes/java/awt/font/OpenType.java +++ b/jdk/src/share/classes/java/awt/font/OpenType.java @@ -31,9 +31,9 @@ package java.awt.font; * sfnt tables from the font. A particular * Font object can implement this interface. *

- * For more information on TrueType fonts, see the - * Apple TrueType Reference Manual - * ( http://fonts.apple.com/TTRefMan/index.html ). + * For more information on TrueType and OpenType fonts, see the + * OpenType specification. + * ( http://www.microsoft.com/typography/otspec/l ). */ public interface OpenType { diff --git a/jdk/src/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java b/jdk/src/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java index e589a7864cb..bb2e2d93fc3 100644 --- a/jdk/src/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java +++ b/jdk/src/share/classes/javax/print/attribute/standard/ReferenceUriSchemesSupported.java @@ -44,7 +44,7 @@ import javax.print.attribute.Attribute; * print request's, print job's, or print service's attribute set. *

* The Internet Assigned Numbers Authority maintains the - * official + * official * list of URI schemes. *

* Class ReferenceUriSchemesSupported defines enumeration values for widely From 34f12e0ac4352a830119db28a80946703d6036b7 Mon Sep 17 00:00:00 2001 From: Jennifer Godinez Date: Thu, 10 Apr 2008 13:57:15 -0700 Subject: [PATCH 06/40] 6678161: Printing to remote non-Postscript printer does not work in Linux Reviewed-by: prr, tdv --- .../classes/sun/print/CUPSPrinter.java | 4 +- .../classes/sun/print/IPPPrintService.java | 63 +++++++++++++++---- .../sun/print/UnixPrintServiceLookup.java | 32 +++++----- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/jdk/src/solaris/classes/sun/print/CUPSPrinter.java b/jdk/src/solaris/classes/sun/print/CUPSPrinter.java index d05a89fff5f..321cf9041c8 100644 --- a/jdk/src/solaris/classes/sun/print/CUPSPrinter.java +++ b/jdk/src/solaris/classes/sun/print/CUPSPrinter.java @@ -333,7 +333,7 @@ public class CUPSPrinter { AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE, new AttributeClass("requested-attributes", AttributeClass.TAG_KEYWORD, - "printer-name") + "printer-uri-supported") }; if (IPPPrintService.writeIPPRequest(os, @@ -354,7 +354,7 @@ public class CUPSPrinter { ArrayList printerNames = new ArrayList(); for (int i=0; i< responseMap.length; i++) { AttributeClass attribClass = (AttributeClass) - responseMap[i].get("printer-name"); + responseMap[i].get("printer-uri-supported"); if (attribClass != null) { String nameStr = attribClass.getStringValue(); diff --git a/jdk/src/solaris/classes/sun/print/IPPPrintService.java b/jdk/src/solaris/classes/sun/print/IPPPrintService.java index 7c63b0d3dc3..6dbb8131ca9 100644 --- a/jdk/src/solaris/classes/sun/print/IPPPrintService.java +++ b/jdk/src/solaris/classes/sun/print/IPPPrintService.java @@ -335,6 +335,38 @@ public class IPPPrintService implements PrintService, SunPrinterJobService { } + IPPPrintService(String name, String uriStr, boolean isCups) { + if ((name == null) || (uriStr == null)){ + throw new IllegalArgumentException("null uri or printer name"); + } + printer = name; + supportedDocFlavors = null; + supportedCats = null; + mediaSizeNames = null; + customMediaSizeNames = null; + mediaTrays = null; + cps = null; + init = false; + defaultMediaIndex = -1; + try { + myURL = + new URL(uriStr.replaceFirst("ipp", "http")); + } catch (Exception e) { + IPPPrintService.debug_println(debugPrefix+ + " IPPPrintService, myURL="+ + myURL+" Exception= "+ + e); + } + + isCupsPrinter = isCups; + try { + myURI = new URI(uriStr); + debug_println(debugPrefix+"IPPPrintService myURI : "+myURI); + } catch (java.net.URISyntaxException e) { + throw new IllegalArgumentException("invalid uri"); + } + } + /* * Initialize mediaSizeNames, mediaTrays and other attributes. @@ -375,7 +407,7 @@ public class IPPPrintService implements PrintService, SunPrinterJobService { return; } catch (Exception e) { IPPPrintService.debug_println(debugPrefix+ - " error creating CUPSPrinter"); + " error creating CUPSPrinter e="+e); } } @@ -807,6 +839,18 @@ public class IPPPrintService implements PrintService, SunPrinterJobService { docList.addAll(Arrays.asList(flavors)); + if (isCupsPrinter) { + /* + Always add Pageable and Printable for CUPS + since it uses Filters to convert from Postscript + to device printer language. + */ + docList.add( + DocFlavor.SERVICE_FORMATTED.PAGEABLE); + docList.add( + DocFlavor.SERVICE_FORMATTED.PRINTABLE); + } + if (mimeType.equals("text/plain") && addHostEncoding) { docList.add(Arrays.asList(textPlainHost)); @@ -820,11 +864,6 @@ public class IPPPrintService implements PrintService, SunPrinterJobService { } else if (mimeType.equals("image/jpeg")) { jpgImagesAdded = true; } else if (mimeType.indexOf("postscript") != -1) { - docList.add( - DocFlavor.SERVICE_FORMATTED.PAGEABLE); - docList.add( - DocFlavor.SERVICE_FORMATTED.PRINTABLE); - psSupported = true; } break; @@ -841,7 +880,7 @@ public class IPPPrintService implements PrintService, SunPrinterJobService { } // check if we need to add image DocFlavors - if (psSupported) { + if (psSupported || isCupsPrinter) { if (!jpgImagesAdded) { docList.addAll(Arrays.asList(imageJPG)); } @@ -1540,10 +1579,7 @@ public class IPPPrintService implements PrintService, SunPrinterJobService { if (isCupsPrinter) { try { urlConnection = getIPPConnection( - new URL("http://"+ - CUPSPrinter.getServer()+":"+ - CUPSPrinter.getPort()+ - "/printers/"+printer+".ppd")); + new URL(myURL+".ppd")); InputStream is = urlConnection.getInputStream(); if (is != null) { @@ -1559,6 +1595,11 @@ public class IPPPrintService implements PrintService, SunPrinterJobService { } } } catch (java.io.IOException e) { + debug_println(" isPostscript, e= "+e); + /* if PPD is not found, this may be a raw printer + and in this case it is assumed that it is a + Postscript printer */ + // do nothing } } } diff --git a/jdk/src/solaris/classes/sun/print/UnixPrintServiceLookup.java b/jdk/src/solaris/classes/sun/print/UnixPrintServiceLookup.java index ddeea1ad30f..4fa0412db18 100644 --- a/jdk/src/solaris/classes/sun/print/UnixPrintServiceLookup.java +++ b/jdk/src/solaris/classes/sun/print/UnixPrintServiceLookup.java @@ -196,11 +196,20 @@ public class UnixPrintServiceLookup extends PrintServiceLookup // refreshes "printServices" public synchronized void refreshServices() { - String[] printers; /* excludes the default printer */ + /* excludes the default printer */ + String[] printers = null; // array of printer names + String[] printerURIs = null; //array of printer URIs getDefaultPrintService(); if (CUPSPrinter.isCupsRunning()) { - printers = CUPSPrinter.getAllPrinters(); + printerURIs = CUPSPrinter.getAllPrinters(); + if ((printerURIs != null) && (printerURIs.length > 0)) { + printers = new String[printerURIs.length]; + for (int i=0; i Date: Thu, 10 Apr 2008 16:28:45 -0700 Subject: [PATCH 07/40] 6662775: Move imaging and color classes from closed to open Reviewed-by: tdv, campbell --- jdk/make/common/internal/BinaryPlugs.gmk | 49 +- jdk/make/java/awt/Makefile | 12 - .../classes/java/awt/color/CMMException.java | 57 + .../classes/java/awt/color/ColorSpace.java | 611 +++++ .../java/awt/color/ICC_ColorSpace.java | 616 +++++ .../classes/java/awt/color/ICC_Profile.java | 2003 +++++++++++++++++ .../java/awt/color/ICC_ProfileGray.java | 150 ++ .../java/awt/color/ICC_ProfileRGB.java | 282 +++ .../java/awt/image/BandedSampleModel.java | 839 +++++++ .../java/awt/image/ColorConvertOp.java | 1109 +++++++++ .../java/awt/image/ComponentSampleModel.java | 1202 ++++++++++ .../classes/java/awt/image/DataBuffer.java | 535 +++++ .../java/awt/image/DataBufferByte.java | 286 +++ .../classes/java/awt/image/DataBufferInt.java | 284 +++ .../java/awt/image/DataBufferShort.java | 283 +++ .../java/awt/image/DataBufferUShort.java | 318 +++ .../image/MultiPixelPackedSampleModel.java | 699 ++++++ .../share/classes/java/awt/image/Raster.java | 1777 +++++++++++++++ .../classes/java/awt/image/RenderedImage.java | 217 ++ .../classes/java/awt/image/SampleModel.java | 1393 ++++++++++++ .../image/SinglePixelPackedSampleModel.java | 805 +++++++ .../java/awt/image/WritableRaster.java | 741 ++++++ .../java/awt/image/WritableRenderedImage.java | 151 ++ .../ContextualRenderedImageFactory.java | 143 ++ .../awt/image/renderable/RenderContext.java | 272 +++ .../awt/image/renderable/RenderableImage.java | 198 ++ .../image/renderable/RenderableImageOp.java | 350 +++ .../renderable/RenderableImageProducer.java | 219 ++ .../renderable/RenderedImageFactory.java | 78 + 29 files changed, 15620 insertions(+), 59 deletions(-) create mode 100644 jdk/src/share/classes/java/awt/color/CMMException.java create mode 100644 jdk/src/share/classes/java/awt/color/ColorSpace.java create mode 100644 jdk/src/share/classes/java/awt/color/ICC_ColorSpace.java create mode 100644 jdk/src/share/classes/java/awt/color/ICC_Profile.java create mode 100644 jdk/src/share/classes/java/awt/color/ICC_ProfileGray.java create mode 100644 jdk/src/share/classes/java/awt/color/ICC_ProfileRGB.java create mode 100644 jdk/src/share/classes/java/awt/image/BandedSampleModel.java create mode 100644 jdk/src/share/classes/java/awt/image/ColorConvertOp.java create mode 100644 jdk/src/share/classes/java/awt/image/ComponentSampleModel.java create mode 100644 jdk/src/share/classes/java/awt/image/DataBuffer.java create mode 100644 jdk/src/share/classes/java/awt/image/DataBufferByte.java create mode 100644 jdk/src/share/classes/java/awt/image/DataBufferInt.java create mode 100644 jdk/src/share/classes/java/awt/image/DataBufferShort.java create mode 100644 jdk/src/share/classes/java/awt/image/DataBufferUShort.java create mode 100644 jdk/src/share/classes/java/awt/image/MultiPixelPackedSampleModel.java create mode 100644 jdk/src/share/classes/java/awt/image/Raster.java create mode 100644 jdk/src/share/classes/java/awt/image/RenderedImage.java create mode 100644 jdk/src/share/classes/java/awt/image/SampleModel.java create mode 100644 jdk/src/share/classes/java/awt/image/SinglePixelPackedSampleModel.java create mode 100644 jdk/src/share/classes/java/awt/image/WritableRaster.java create mode 100644 jdk/src/share/classes/java/awt/image/WritableRenderedImage.java create mode 100644 jdk/src/share/classes/java/awt/image/renderable/ContextualRenderedImageFactory.java create mode 100644 jdk/src/share/classes/java/awt/image/renderable/RenderContext.java create mode 100644 jdk/src/share/classes/java/awt/image/renderable/RenderableImage.java create mode 100644 jdk/src/share/classes/java/awt/image/renderable/RenderableImageOp.java create mode 100644 jdk/src/share/classes/java/awt/image/renderable/RenderableImageProducer.java create mode 100644 jdk/src/share/classes/java/awt/image/renderable/RenderedImageFactory.java diff --git a/jdk/make/common/internal/BinaryPlugs.gmk b/jdk/make/common/internal/BinaryPlugs.gmk index c4f1060076f..8eeb95d1284 100644 --- a/jdk/make/common/internal/BinaryPlugs.gmk +++ b/jdk/make/common/internal/BinaryPlugs.gmk @@ -126,44 +126,10 @@ com/sun/media/sound/SimpleInputDeviceProvider\$$1.class \ com/sun/media/sound/SimpleInputDeviceProvider\$$InputDeviceInfo.class \ com/sun/media/sound/SimpleInputDeviceProvider.class -PLUG_AWT_CLASS_NAMES = \ -java/awt/color/CMMException.class \ -java/awt/color/ColorSpace.class \ -java/awt/color/ICC_ColorSpace.class \ -java/awt/color/ICC_Profile\$$1.class \ -java/awt/color/ICC_Profile\$$2.class \ -java/awt/color/ICC_Profile\$$3.class \ -java/awt/color/ICC_Profile.class \ -java/awt/color/ICC_ProfileGray.class \ -java/awt/color/ICC_ProfileRGB.class \ -java/awt/image/BandedSampleModel.class \ -java/awt/image/ColorConvertOp.class \ -java/awt/image/ComponentSampleModel.class \ -java/awt/image/DataBuffer\$$1.class \ -java/awt/image/DataBuffer.class \ -java/awt/image/DataBufferByte.class \ -java/awt/image/DataBufferInt.class \ -java/awt/image/DataBufferShort.class \ -java/awt/image/DataBufferUShort.class \ -java/awt/image/MultiPixelPackedSampleModel.class \ -java/awt/image/Raster.class \ -java/awt/image/RenderedImage.class \ -java/awt/image/SampleModel.class \ -java/awt/image/SinglePixelPackedSampleModel.class \ -java/awt/image/WritableRaster.class \ -java/awt/image/WritableRenderedImage.class \ -java/awt/image/renderable/ContextualRenderedImageFactory.class \ -java/awt/image/renderable/ParameterBlock.class \ -java/awt/image/renderable/RenderContext.class \ -java/awt/image/renderable/RenderableImage.class \ -java/awt/image/renderable/RenderableImageOp.class \ -java/awt/image/renderable/RenderableImageProducer.class \ -java/awt/image/renderable/RenderedImageFactory.class - # Class list temp files (used by both import and export of plugs) PLUG_TEMPDIR=$(ABS_TEMPDIR)/plugs -PLUG_CLASS_AREAS = jmf sound awt +PLUG_CLASS_AREAS = jmf sound PLUG_CLISTS = $(PLUG_CLASS_AREAS:%=$(PLUG_TEMPDIR)/%.clist) # Create jargs file command @@ -186,11 +152,6 @@ $(PLUG_TEMPDIR)/sound.clist: @for i in $(PLUG_SOUND_CLASS_NAMES) ; do \ $(ECHO) "$$i" >> $@ ; \ done -$(PLUG_TEMPDIR)/awt.clist: - @$(prep-target) - @for i in $(PLUG_AWT_CLASS_NAMES) ; do \ - $(ECHO) "$$i" >> $@ ; \ - done $(PLUG_TEMPDIR)/all.clist: $(PLUG_CLISTS) @$(prep-target) $(CAT) $(PLUG_CLISTS) > $@ @@ -198,8 +159,6 @@ $(PLUG_TEMPDIR)/jmf.jargs: $(PLUG_TEMPDIR)/jmf.clist $(plug-create-jargs) $(PLUG_TEMPDIR)/sound.jargs: $(PLUG_TEMPDIR)/sound.clist $(plug-create-jargs) -$(PLUG_TEMPDIR)/awt.jargs: $(PLUG_TEMPDIR)/awt.clist - $(plug-create-jargs) $(PLUG_TEMPDIR)/all.jargs: $(PLUG_TEMPDIR)/all.clist $(plug-create-jargs) @@ -235,15 +194,12 @@ import-binary-plug-jmf-classes: $(PLUG_IMPORT_JARFILE) $(PLUG_TEMPDIR)/jmf.clist $(call import-binary-plug-classes,$(PLUG_TEMPDIR)/jmf.clist) import-binary-plug-sound-classes: $(PLUG_IMPORT_JARFILE) $(PLUG_TEMPDIR)/sound.clist $(call import-binary-plug-classes,$(PLUG_TEMPDIR)/sound.clist) -import-binary-plug-awt-classes: $(PLUG_IMPORT_JARFILE) $(PLUG_TEMPDIR)/awt.clist - $(call import-binary-plug-classes,$(PLUG_TEMPDIR)/awt.clist) # Import all classes from the jar file import-binary-plug-jar: \ import-binary-plug-jmf-classes \ - import-binary-plug-sound-classes \ - import-binary-plug-awt-classes + import-binary-plug-sound-classes # Import native libraries @@ -286,7 +242,6 @@ import-binary-plugs: \ import-binary-plug-jar \ import-binary-plug-jmf-classes \ import-binary-plug-sound-classes \ - import-binary-plug-awt-classes \ import-binary-plug-jsound-library else # !OPENJDK diff --git a/jdk/make/java/awt/Makefile b/jdk/make/java/awt/Makefile index f59ea7817a8..579aa58cff6 100644 --- a/jdk/make/java/awt/Makefile +++ b/jdk/make/java/awt/Makefile @@ -28,24 +28,12 @@ PACKAGE = java.awt PRODUCT = sun include $(BUILDDIR)/common/Defs.gmk -# WARNING: Make sure the OPENJDK plugs are up-to-date, see make/common/internal/BinaryPlugs.gmk # # Files # AUTO_FILES_JAVA_DIRS = java/awt sun/awt/geom -# -# Specific to OPENJDK -# -ifdef OPENJDK - -build: import-binary-plug-awt-classes - -include $(BUILDDIR)/common/internal/BinaryPlugs.gmk - -endif - build: properties cursors # diff --git a/jdk/src/share/classes/java/awt/color/CMMException.java b/jdk/src/share/classes/java/awt/color/CMMException.java new file mode 100644 index 00000000000..5632c1b3482 --- /dev/null +++ b/jdk/src/share/classes/java/awt/color/CMMException.java @@ -0,0 +1,57 @@ +/* + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + Created by gbp, October 25, 1997 + + * + */ +/********************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + + +package java.awt.color; + + +/** + * This exception is thrown if the native CMM returns an error. + */ + +public class CMMException extends java.lang.RuntimeException { + + /** + * Constructs a CMMException with the specified detail message. + * @param s the specified detail message + */ + public CMMException (String s) { + super (s); + } +} diff --git a/jdk/src/share/classes/java/awt/color/ColorSpace.java b/jdk/src/share/classes/java/awt/color/ColorSpace.java new file mode 100644 index 00000000000..a38cf377ea0 --- /dev/null +++ b/jdk/src/share/classes/java/awt/color/ColorSpace.java @@ -0,0 +1,611 @@ +/* + * Portions Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/********************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.color; + +import sun.java2d.cmm.PCMM; +import sun.java2d.cmm.CMSManager; + + +/** + * This abstract class is used to serve as a color space tag to identify the + * specific color space of a Color object or, via a ColorModel object, + * of an Image, a BufferedImage, or a GraphicsDevice. It contains + * methods that transform colors in a specific color space to/from sRGB + * and to/from a well-defined CIEXYZ color space. + *

+ * For purposes of the methods in this class, colors are represented as + * arrays of color components represented as floats in a normalized range + * defined by each ColorSpace. For many ColorSpaces (e.g. sRGB), this + * range is 0.0 to 1.0. However, some ColorSpaces have components whose + * values have a different range. Methods are provided to inquire per + * component minimum and maximum normalized values. + *

+ * Several variables are defined for purposes of referring to color + * space types (e.g. TYPE_RGB, TYPE_XYZ, etc.) and to refer to specific + * color spaces (e.g. CS_sRGB and CS_CIEXYZ). + * sRGB is a proposed standard RGB color space. For more information, + * see + * http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html + * . + *

+ * The purpose of the methods to transform to/from the well-defined + * CIEXYZ color space is to support conversions between any two color + * spaces at a reasonably high degree of accuracy. It is expected that + * particular implementations of subclasses of ColorSpace (e.g. + * ICC_ColorSpace) will support high performance conversion based on + * underlying platform color management systems. + *

+ * The CS_CIEXYZ space used by the toCIEXYZ/fromCIEXYZ methods can be + * described as follows: +

+
+      CIEXYZ
+      viewing illuminance: 200 lux
+      viewing white point: CIE D50
+      media white point: "that of a perfectly reflecting diffuser" -- D50
+      media black point: 0 lux or 0 Reflectance
+      flare: 1 percent
+      surround: 20percent of the media white point
+      media description: reflection print (i.e., RLAB, Hunt viewing media)
+      note: For developers creating an ICC profile for this conversion
+            space, the following is applicable.  Use a simple Von Kries
+            white point adaptation folded into the 3X3 matrix parameters
+            and fold the flare and surround effects into the three
+            one-dimensional lookup tables (assuming one uses the minimal
+            model for monitors).
+
+
+ * + *

+ * @see ICC_ColorSpace + */ + + + +public abstract class ColorSpace implements java.io.Serializable { + + static final long serialVersionUID = -409452704308689724L; + + private int type; + private int numComponents; + private transient String [] compName = null; + + // Cache of singletons for the predefined color spaces. + private static ColorSpace sRGBspace; + private static ColorSpace XYZspace; + private static ColorSpace PYCCspace; + private static ColorSpace GRAYspace; + private static ColorSpace LINEAR_RGBspace; + + /** + * Any of the family of XYZ color spaces. + */ + public static final int TYPE_XYZ = 0; + + /** + * Any of the family of Lab color spaces. + */ + public static final int TYPE_Lab = 1; + + /** + * Any of the family of Luv color spaces. + */ + public static final int TYPE_Luv = 2; + + /** + * Any of the family of YCbCr color spaces. + */ + public static final int TYPE_YCbCr = 3; + + /** + * Any of the family of Yxy color spaces. + */ + public static final int TYPE_Yxy = 4; + + /** + * Any of the family of RGB color spaces. + */ + public static final int TYPE_RGB = 5; + + /** + * Any of the family of GRAY color spaces. + */ + public static final int TYPE_GRAY = 6; + + /** + * Any of the family of HSV color spaces. + */ + public static final int TYPE_HSV = 7; + + /** + * Any of the family of HLS color spaces. + */ + public static final int TYPE_HLS = 8; + + /** + * Any of the family of CMYK color spaces. + */ + public static final int TYPE_CMYK = 9; + + /** + * Any of the family of CMY color spaces. + */ + public static final int TYPE_CMY = 11; + + /** + * Generic 2 component color spaces. + */ + public static final int TYPE_2CLR = 12; + + /** + * Generic 3 component color spaces. + */ + public static final int TYPE_3CLR = 13; + + /** + * Generic 4 component color spaces. + */ + public static final int TYPE_4CLR = 14; + + /** + * Generic 5 component color spaces. + */ + public static final int TYPE_5CLR = 15; + + /** + * Generic 6 component color spaces. + */ + public static final int TYPE_6CLR = 16; + + /** + * Generic 7 component color spaces. + */ + public static final int TYPE_7CLR = 17; + + /** + * Generic 8 component color spaces. + */ + public static final int TYPE_8CLR = 18; + + /** + * Generic 9 component color spaces. + */ + public static final int TYPE_9CLR = 19; + + /** + * Generic 10 component color spaces. + */ + public static final int TYPE_ACLR = 20; + + /** + * Generic 11 component color spaces. + */ + public static final int TYPE_BCLR = 21; + + /** + * Generic 12 component color spaces. + */ + public static final int TYPE_CCLR = 22; + + /** + * Generic 13 component color spaces. + */ + public static final int TYPE_DCLR = 23; + + /** + * Generic 14 component color spaces. + */ + public static final int TYPE_ECLR = 24; + + /** + * Generic 15 component color spaces. + */ + public static final int TYPE_FCLR = 25; + + /** + * The sRGB color space defined at + * + * http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html + * . + */ + public static final int CS_sRGB = 1000; + + /** + * A built-in linear RGB color space. This space is based on the + * same RGB primaries as CS_sRGB, but has a linear tone reproduction curve. + */ + public static final int CS_LINEAR_RGB = 1004; + + /** + * The CIEXYZ conversion color space defined above. + */ + public static final int CS_CIEXYZ = 1001; + + /** + * The Photo YCC conversion color space. + */ + public static final int CS_PYCC = 1002; + + /** + * The built-in linear gray scale color space. + */ + public static final int CS_GRAY = 1003; + + + /** + * Constructs a ColorSpace object given a color space type + * and the number of components. + * @param type one of the ColorSpace type constants + * @param numcomponents the number of components in the color space + */ + protected ColorSpace (int type, int numcomponents) { + this.type = type; + this.numComponents = numcomponents; + } + + + /** + * Returns a ColorSpace representing one of the specific + * predefined color spaces. + * @param colorspace a specific color space identified by one of + * the predefined class constants (e.g. CS_sRGB, CS_LINEAR_RGB, + * CS_CIEXYZ, CS_GRAY, or CS_PYCC) + * @return the requested ColorSpace object + */ + // NOTE: This method may be called by privileged threads. + // DO NOT INVOKE CLIENT CODE ON THIS THREAD! + public static ColorSpace getInstance (int colorspace) + { + ColorSpace theColorSpace; + + switch (colorspace) { + case CS_sRGB: + synchronized(ColorSpace.class) { + if (sRGBspace == null) { + ICC_Profile theProfile = ICC_Profile.getInstance (CS_sRGB); + sRGBspace = new ICC_ColorSpace (theProfile); + } + + theColorSpace = sRGBspace; + } + break; + + case CS_CIEXYZ: + synchronized(ColorSpace.class) { + if (XYZspace == null) { + ICC_Profile theProfile = + ICC_Profile.getInstance (CS_CIEXYZ); + XYZspace = new ICC_ColorSpace (theProfile); + } + + theColorSpace = XYZspace; + } + break; + + case CS_PYCC: + synchronized(ColorSpace.class) { + if (PYCCspace == null) { + ICC_Profile theProfile = ICC_Profile.getInstance (CS_PYCC); + PYCCspace = new ICC_ColorSpace (theProfile); + } + + theColorSpace = PYCCspace; + } + break; + + + case CS_GRAY: + synchronized(ColorSpace.class) { + if (GRAYspace == null) { + ICC_Profile theProfile = ICC_Profile.getInstance (CS_GRAY); + GRAYspace = new ICC_ColorSpace (theProfile); + /* to allow access from java.awt.ColorModel */ + CMSManager.GRAYspace = GRAYspace; + } + + theColorSpace = GRAYspace; + } + break; + + + case CS_LINEAR_RGB: + synchronized(ColorSpace.class) { + if (LINEAR_RGBspace == null) { + ICC_Profile theProfile = + ICC_Profile.getInstance(CS_LINEAR_RGB); + LINEAR_RGBspace = new ICC_ColorSpace (theProfile); + /* to allow access from java.awt.ColorModel */ + CMSManager.LINEAR_RGBspace = LINEAR_RGBspace; + } + + theColorSpace = LINEAR_RGBspace; + } + break; + + + default: + throw new IllegalArgumentException ("Unknown color space"); + } + + return theColorSpace; + } + + + /** + * Returns true if the ColorSpace is CS_sRGB. + * @return true if this is a CS_sRGB color + * space, false if it is not + */ + public boolean isCS_sRGB () { + /* REMIND - make sure we know sRGBspace exists already */ + return (this == sRGBspace); + } + + /** + * Transforms a color value assumed to be in this ColorSpace + * into a value in the default CS_sRGB color space. + *

+ * This method transforms color values using algorithms designed + * to produce the best perceptual match between input and output + * colors. In order to do colorimetric conversion of color values, + * you should use the toCIEXYZ + * method of this color space to first convert from the input + * color space to the CS_CIEXYZ color space, and then use the + * fromCIEXYZ method of the CS_sRGB color space to + * convert from CS_CIEXYZ to the output color space. + * See {@link #toCIEXYZ(float[]) toCIEXYZ} and + * {@link #fromCIEXYZ(float[]) fromCIEXYZ} for further information. + *

+ * @param colorvalue a float array with length of at least the number + * of components in this ColorSpace + * @return a float array of length 3 + * @throws ArrayIndexOutOfBoundsException if array length is not + * at least the number of components in this ColorSpace + */ + public abstract float[] toRGB(float[] colorvalue); + + + /** + * Transforms a color value assumed to be in the default CS_sRGB + * color space into this ColorSpace. + *

+ * This method transforms color values using algorithms designed + * to produce the best perceptual match between input and output + * colors. In order to do colorimetric conversion of color values, + * you should use the toCIEXYZ + * method of the CS_sRGB color space to first convert from the input + * color space to the CS_CIEXYZ color space, and then use the + * fromCIEXYZ method of this color space to + * convert from CS_CIEXYZ to the output color space. + * See {@link #toCIEXYZ(float[]) toCIEXYZ} and + * {@link #fromCIEXYZ(float[]) fromCIEXYZ} for further information. + *

+ * @param rgbvalue a float array with length of at least 3 + * @return a float array with length equal to the number of + * components in this ColorSpace + * @throws ArrayIndexOutOfBoundsException if array length is not + * at least 3 + */ + public abstract float[] fromRGB(float[] rgbvalue); + + + /** + * Transforms a color value assumed to be in this ColorSpace + * into the CS_CIEXYZ conversion color space. + *

+ * This method transforms color values using relative colorimetry, + * as defined by the International Color Consortium standard. This + * means that the XYZ values returned by this method are represented + * relative to the D50 white point of the CS_CIEXYZ color space. + * This representation is useful in a two-step color conversion + * process in which colors are transformed from an input color + * space to CS_CIEXYZ and then to an output color space. This + * representation is not the same as the XYZ values that would + * be measured from the given color value by a colorimeter. + * A further transformation is necessary to compute the XYZ values + * that would be measured using current CIE recommended practices. + * See the {@link ICC_ColorSpace#toCIEXYZ(float[]) toCIEXYZ} method of + * ICC_ColorSpace for further information. + *

+ * @param colorvalue a float array with length of at least the number + * of components in this ColorSpace + * @return a float array of length 3 + * @throws ArrayIndexOutOfBoundsException if array length is not + * at least the number of components in this ColorSpace. + */ + public abstract float[] toCIEXYZ(float[] colorvalue); + + + /** + * Transforms a color value assumed to be in the CS_CIEXYZ conversion + * color space into this ColorSpace. + *

+ * This method transforms color values using relative colorimetry, + * as defined by the International Color Consortium standard. This + * means that the XYZ argument values taken by this method are represented + * relative to the D50 white point of the CS_CIEXYZ color space. + * This representation is useful in a two-step color conversion + * process in which colors are transformed from an input color + * space to CS_CIEXYZ and then to an output color space. The color + * values returned by this method are not those that would produce + * the XYZ value passed to the method when measured by a colorimeter. + * If you have XYZ values corresponding to measurements made using + * current CIE recommended practices, they must be converted to D50 + * relative values before being passed to this method. + * See the {@link ICC_ColorSpace#fromCIEXYZ(float[]) fromCIEXYZ} method of + * ICC_ColorSpace for further information. + *

+ * @param colorvalue a float array with length of at least 3 + * @return a float array with length equal to the number of + * components in this ColorSpace + * @throws ArrayIndexOutOfBoundsException if array length is not + * at least 3 + */ + public abstract float[] fromCIEXYZ(float[] colorvalue); + + /** + * Returns the color space type of this ColorSpace (for example + * TYPE_RGB, TYPE_XYZ, ...). The type defines the + * number of components of the color space and the interpretation, + * e.g. TYPE_RGB identifies a color space with three components - red, + * green, and blue. It does not define the particular color + * characteristics of the space, e.g. the chromaticities of the + * primaries. + * + * @return the type constant that represents the type of this + * ColorSpace + */ + public int getType() { + return type; + } + + /** + * Returns the number of components of this ColorSpace. + * @return The number of components in this ColorSpace. + */ + public int getNumComponents() { + return numComponents; + } + + /** + * Returns the name of the component given the component index. + * + * @param idx the component index + * @return the name of the component at the specified index + * @throws IllegalArgumentException if idx is + * less than 0 or greater than numComponents - 1 + */ + public String getName (int idx) { + /* REMIND - handle common cases here */ + if ((idx < 0) || (idx > numComponents - 1)) { + throw new IllegalArgumentException( + "Component index out of range: " + idx); + } + + if (compName == null) { + switch (type) { + case ColorSpace.TYPE_XYZ: + compName = new String[] {"X", "Y", "Z"}; + break; + case ColorSpace.TYPE_Lab: + compName = new String[] {"L", "a", "b"}; + break; + case ColorSpace.TYPE_Luv: + compName = new String[] {"L", "u", "v"}; + break; + case ColorSpace.TYPE_YCbCr: + compName = new String[] {"Y", "Cb", "Cr"}; + break; + case ColorSpace.TYPE_Yxy: + compName = new String[] {"Y", "x", "y"}; + break; + case ColorSpace.TYPE_RGB: + compName = new String[] {"Red", "Green", "Blue"}; + break; + case ColorSpace.TYPE_GRAY: + compName = new String[] {"Gray"}; + break; + case ColorSpace.TYPE_HSV: + compName = new String[] {"Hue", "Saturation", "Value"}; + break; + case ColorSpace.TYPE_HLS: + compName = new String[] {"Hue", "Lightness", + "Saturation"}; + break; + case ColorSpace.TYPE_CMYK: + compName = new String[] {"Cyan", "Magenta", "Yellow", + "Black"}; + break; + case ColorSpace.TYPE_CMY: + compName = new String[] {"Cyan", "Magenta", "Yellow"}; + break; + default: + String [] tmp = new String[numComponents]; + for (int i = 0; i < tmp.length; i++) { + tmp[i] = "Unnamed color component(" + i + ")"; + } + compName = tmp; + } + } + return compName[idx]; + } + + /** + * Returns the minimum normalized color component value for the + * specified component. The default implementation in this abstract + * class returns 0.0 for all components. Subclasses should override + * this method if necessary. + * + * @param component the component index + * @return the minimum normalized component value + * @throws IllegalArgumentException if component is less than 0 or + * greater than numComponents - 1 + * @since 1.4 + */ + public float getMinValue(int component) { + if ((component < 0) || (component > numComponents - 1)) { + throw new IllegalArgumentException( + "Component index out of range: " + component); + } + return 0.0f; + } + + /** + * Returns the maximum normalized color component value for the + * specified component. The default implementation in this abstract + * class returns 1.0 for all components. Subclasses should override + * this method if necessary. + * + * @param component the component index + * @return the maximum normalized component value + * @throws IllegalArgumentException if component is less than 0 or + * greater than numComponents - 1 + * @since 1.4 + */ + public float getMaxValue(int component) { + if ((component < 0) || (component > numComponents - 1)) { + throw new IllegalArgumentException( + "Component index out of range: " + component); + } + return 1.0f; + } + + /* Returns true if cspace is the XYZspace. + */ + static boolean isCS_CIEXYZ(ColorSpace cspace) { + return (cspace == XYZspace); + } +} diff --git a/jdk/src/share/classes/java/awt/color/ICC_ColorSpace.java b/jdk/src/share/classes/java/awt/color/ICC_ColorSpace.java new file mode 100644 index 00000000000..0250a3744f0 --- /dev/null +++ b/jdk/src/share/classes/java/awt/color/ICC_ColorSpace.java @@ -0,0 +1,616 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/********************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.color; + +import sun.java2d.cmm.ColorTransform; +import sun.java2d.cmm.CMSManager; +import sun.java2d.cmm.PCMM; + + +/** + * + * The ICC_ColorSpace class is an implementation of the abstract + * ColorSpace class. This representation of + * device independent and device dependent color spaces is based on the + * International Color Consortium Specification ICC.1:2001-12, File Format for + * Color Profiles (see http://www.color.org). + *

+ * Typically, a Color or ColorModel would be associated with an ICC + * Profile which is either an input, display, or output profile (see + * the ICC specification). There are other types of ICC Profiles, e.g. + * abstract profiles, device link profiles, and named color profiles, + * which do not contain information appropriate for representing the color + * space of a color, image, or device (see ICC_Profile). + * Attempting to create an ICC_ColorSpace object from an inappropriate ICC + * Profile is an error. + *

+ * ICC Profiles represent transformations from the color space of + * the profile (e.g. a monitor) to a Profile Connection Space (PCS). + * Profiles of interest for tagging images or colors have a + * PCS which is one of the device independent + * spaces (one CIEXYZ space and two CIELab spaces) defined in the + * ICC Profile Format Specification. Most profiles of interest + * either have invertible transformations or explicitly specify + * transformations going both directions. Should an ICC_ColorSpace + * object be used in a way requiring a conversion from PCS to + * the profile's native space and there is inadequate data to + * correctly perform the conversion, the ICC_ColorSpace object will + * produce output in the specified type of color space (e.g. TYPE_RGB, + * TYPE_CMYK, etc.), but the specific color values of the output data + * will be undefined. + *

+ * The details of this class are not important for simple applets, + * which draw in a default color space or manipulate and display + * imported images with a known color space. At most, such applets + * would need to get one of the default color spaces via + * ColorSpace.getInstance(). + *

+ * @see ColorSpace + * @see ICC_Profile + */ + + + +public class ICC_ColorSpace extends ColorSpace { + + static final long serialVersionUID = 3455889114070431483L; + + private ICC_Profile thisProfile; + private float[] minVal; + private float[] maxVal; + private float[] diffMinMax; + private float[] invDiffMinMax; + private boolean needScaleInit = true; + + // {to,from}{RGB,CIEXYZ} methods create and cache these when needed + private transient ColorTransform this2srgb; + private transient ColorTransform srgb2this; + private transient ColorTransform this2xyz; + private transient ColorTransform xyz2this; + + + /** + * Constructs a new ICC_ColorSpace from an ICC_Profile object. + * @param profile the specified ICC_Profile object + * @exception IllegalArgumentException if profile is inappropriate for + * representing a ColorSpace. + */ + public ICC_ColorSpace (ICC_Profile profile) { + super (profile.getColorSpaceType(), profile.getNumComponents()); + + int profileClass = profile.getProfileClass(); + + /* REMIND - is NAMEDCOLOR OK? */ + if ((profileClass != ICC_Profile.CLASS_INPUT) && + (profileClass != ICC_Profile.CLASS_DISPLAY) && + (profileClass != ICC_Profile.CLASS_OUTPUT) && + (profileClass != ICC_Profile.CLASS_COLORSPACECONVERSION) && + (profileClass != ICC_Profile.CLASS_NAMEDCOLOR) && + (profileClass != ICC_Profile.CLASS_ABSTRACT)) { + throw new IllegalArgumentException("Invalid profile type"); + } + + thisProfile = profile; + setMinMax(); + } + + /** + * Returns the ICC_Profile for this ICC_ColorSpace. + * @return the ICC_Profile for this ICC_ColorSpace. + */ + public ICC_Profile getProfile() { + return thisProfile; + } + + /** + * Transforms a color value assumed to be in this ColorSpace + * into a value in the default CS_sRGB color space. + *

+ * This method transforms color values using algorithms designed + * to produce the best perceptual match between input and output + * colors. In order to do colorimetric conversion of color values, + * you should use the toCIEXYZ + * method of this color space to first convert from the input + * color space to the CS_CIEXYZ color space, and then use the + * fromCIEXYZ method of the CS_sRGB color space to + * convert from CS_CIEXYZ to the output color space. + * See {@link #toCIEXYZ(float[]) toCIEXYZ} and + * {@link #fromCIEXYZ(float[]) fromCIEXYZ} for further information. + *

+ * @param colorvalue a float array with length of at least the number + * of components in this ColorSpace. + * @return a float array of length 3. + * @throws ArrayIndexOutOfBoundsException if array length is not + * at least the number of components in this ColorSpace. + */ + public float[] toRGB (float[] colorvalue) { + + if (this2srgb == null) { + ColorTransform[] transformList = new ColorTransform [2]; + ICC_ColorSpace srgbCS = + (ICC_ColorSpace) ColorSpace.getInstance (CS_sRGB); + PCMM mdl = CMSManager.getModule(); + transformList[0] = mdl.createTransform( + thisProfile, ColorTransform.Any, ColorTransform.In); + transformList[1] = mdl.createTransform( + srgbCS.getProfile(), ColorTransform.Any, ColorTransform.Out); + this2srgb = mdl.createTransform(transformList); + if (needScaleInit) { + setComponentScaling(); + } + } + + int nc = this.getNumComponents(); + short tmp[] = new short[nc]; + for (int i = 0; i < nc; i++) { + tmp[i] = (short) + ((colorvalue[i] - minVal[i]) * invDiffMinMax[i] + 0.5f); + } + tmp = this2srgb.colorConvert(tmp, null); + float[] result = new float [3]; + for (int i = 0; i < 3; i++) { + result[i] = ((float) (tmp[i] & 0xffff)) / 65535.0f; + } + return result; + } + + /** + * Transforms a color value assumed to be in the default CS_sRGB + * color space into this ColorSpace. + *

+ * This method transforms color values using algorithms designed + * to produce the best perceptual match between input and output + * colors. In order to do colorimetric conversion of color values, + * you should use the toCIEXYZ + * method of the CS_sRGB color space to first convert from the input + * color space to the CS_CIEXYZ color space, and then use the + * fromCIEXYZ method of this color space to + * convert from CS_CIEXYZ to the output color space. + * See {@link #toCIEXYZ(float[]) toCIEXYZ} and + * {@link #fromCIEXYZ(float[]) fromCIEXYZ} for further information. + *

+ * @param rgbvalue a float array with length of at least 3. + * @return a float array with length equal to the number of + * components in this ColorSpace. + * @throws ArrayIndexOutOfBoundsException if array length is not + * at least 3. + */ + public float[] fromRGB(float[] rgbvalue) { + + if (srgb2this == null) { + ColorTransform[] transformList = new ColorTransform [2]; + ICC_ColorSpace srgbCS = + (ICC_ColorSpace) ColorSpace.getInstance (CS_sRGB); + PCMM mdl = CMSManager.getModule(); + transformList[0] = mdl.createTransform( + srgbCS.getProfile(), ColorTransform.Any, ColorTransform.In); + transformList[1] = mdl.createTransform( + thisProfile, ColorTransform.Any, ColorTransform.Out); + srgb2this = mdl.createTransform(transformList); + if (needScaleInit) { + setComponentScaling(); + } + } + + short tmp[] = new short[3]; + for (int i = 0; i < 3; i++) { + tmp[i] = (short) ((rgbvalue[i] * 65535.0f) + 0.5f); + } + tmp = srgb2this.colorConvert(tmp, null); + int nc = this.getNumComponents(); + float[] result = new float [nc]; + for (int i = 0; i < nc; i++) { + result[i] = (((float) (tmp[i] & 0xffff)) / 65535.0f) * + diffMinMax[i] + minVal[i]; + } + return result; + } + + + /** + * Transforms a color value assumed to be in this ColorSpace + * into the CS_CIEXYZ conversion color space. + *

+ * This method transforms color values using relative colorimetry, + * as defined by the ICC Specification. This + * means that the XYZ values returned by this method are represented + * relative to the D50 white point of the CS_CIEXYZ color space. + * This representation is useful in a two-step color conversion + * process in which colors are transformed from an input color + * space to CS_CIEXYZ and then to an output color space. This + * representation is not the same as the XYZ values that would + * be measured from the given color value by a colorimeter. + * A further transformation is necessary to compute the XYZ values + * that would be measured using current CIE recommended practices. + * The paragraphs below explain this in more detail. + *

+ * The ICC standard uses a device independent color space (DICS) as the + * mechanism for converting color from one device to another device. In + * this architecture, colors are converted from the source device's color + * space to the ICC DICS and then from the ICC DICS to the destination + * device's color space. The ICC standard defines device profiles which + * contain transforms which will convert between a device's color space + * and the ICC DICS. The overall conversion of colors from a source + * device to colors of a destination device is done by connecting the + * device-to-DICS transform of the profile for the source device to the + * DICS-to-device transform of the profile for the destination device. + * For this reason, the ICC DICS is commonly referred to as the profile + * connection space (PCS). The color space used in the methods + * toCIEXYZ and fromCIEXYZ is the CIEXYZ PCS defined by the ICC + * Specification. This is also the color space represented by + * ColorSpace.CS_CIEXYZ. + *

+ * The XYZ values of a color are often represented as relative to some + * white point, so the actual meaning of the XYZ values cannot be known + * without knowing the white point of those values. This is known as + * relative colorimetry. The PCS uses a white point of D50, so the XYZ + * values of the PCS are relative to D50. For example, white in the PCS + * will have the XYZ values of D50, which is defined to be X=.9642, + * Y=1.000, and Z=0.8249. This white point is commonly used for graphic + * arts applications, but others are often used in other applications. + *

+ * To quantify the color characteristics of a device such as a printer + * or monitor, measurements of XYZ values for particular device colors + * are typically made. For purposes of this discussion, the term + * device XYZ values is used to mean the XYZ values that would be + * measured from device colors using current CIE recommended practices. + *

+ * Converting between device XYZ values and the PCS XYZ values returned + * by this method corresponds to converting between the device's color + * space, as represented by CIE colorimetric values, and the PCS. There + * are many factors involved in this process, some of which are quite + * subtle. The most important, however, is the adjustment made to account + * for differences between the device's white point and the white point of + * the PCS. There are many techniques for doing this and it is the + * subject of much current research and controversy. Some commonly used + * methods are XYZ scaling, the von Kries transform, and the Bradford + * transform. The proper method to use depends upon each particular + * application. + *

+ * The simplest method is XYZ scaling. In this method each device XYZ + * value is converted to a PCS XYZ value by multiplying it by the ratio + * of the PCS white point (D50) to the device white point. + *

+     *
+     * Xd, Yd, Zd are the device XYZ values
+     * Xdw, Ydw, Zdw are the device XYZ white point values
+     * Xp, Yp, Zp are the PCS XYZ values
+     * Xd50, Yd50, Zd50 are the PCS XYZ white point values
+     *
+     * Xp = Xd * (Xd50 / Xdw)
+     * Yp = Yd * (Yd50 / Ydw)
+     * Zp = Zd * (Zd50 / Zdw)
+     *
+     * 
+ *

+ * Conversion from the PCS to the device would be done by inverting these + * equations: + *

+     *
+     * Xd = Xp * (Xdw / Xd50)
+     * Yd = Yp * (Ydw / Yd50)
+     * Zd = Zp * (Zdw / Zd50)
+     *
+     * 
+ *

+ * Note that the media white point tag in an ICC profile is not the same + * as the device white point. The media white point tag is expressed in + * PCS values and is used to represent the difference between the XYZ of + * device illuminant and the XYZ of the device media when measured under + * that illuminant. The device white point is expressed as the device + * XYZ values corresponding to white displayed on the device. For + * example, displaying the RGB color (1.0, 1.0, 1.0) on an sRGB device + * will result in a measured device XYZ value of D65. This will not + * be the same as the media white point tag XYZ value in the ICC + * profile for an sRGB device. + *

+ * @param colorvalue a float array with length of at least the number + * of components in this ColorSpace. + * @return a float array of length 3. + * @throws ArrayIndexOutOfBoundsException if array length is not + * at least the number of components in this ColorSpace. + */ + public float[] toCIEXYZ(float[] colorvalue) { + + if (this2xyz == null) { + ColorTransform[] transformList = new ColorTransform [2]; + ICC_ColorSpace xyzCS = + (ICC_ColorSpace) ColorSpace.getInstance (CS_CIEXYZ); + PCMM mdl = CMSManager.getModule(); + try { + transformList[0] = mdl.createTransform( + thisProfile, ICC_Profile.icRelativeColorimetric, + ColorTransform.In); + } catch (CMMException e) { + transformList[0] = mdl.createTransform( + thisProfile, ColorTransform.Any, ColorTransform.In); + } + transformList[1] = mdl.createTransform( + xyzCS.getProfile(), ColorTransform.Any, ColorTransform.Out); + this2xyz = mdl.createTransform (transformList); + if (needScaleInit) { + setComponentScaling(); + } + } + + int nc = this.getNumComponents(); + short tmp[] = new short[nc]; + for (int i = 0; i < nc; i++) { + tmp[i] = (short) + ((colorvalue[i] - minVal[i]) * invDiffMinMax[i] + 0.5f); + } + tmp = this2xyz.colorConvert(tmp, null); + float ALMOST_TWO = 1.0f + (32767.0f / 32768.0f); + // For CIEXYZ, min = 0.0, max = ALMOST_TWO for all components + float[] result = new float [3]; + for (int i = 0; i < 3; i++) { + result[i] = (((float) (tmp[i] & 0xffff)) / 65535.0f) * ALMOST_TWO; + } + return result; + } + + + /** + * Transforms a color value assumed to be in the CS_CIEXYZ conversion + * color space into this ColorSpace. + *

+ * This method transforms color values using relative colorimetry, + * as defined by the ICC Specification. This + * means that the XYZ argument values taken by this method are represented + * relative to the D50 white point of the CS_CIEXYZ color space. + * This representation is useful in a two-step color conversion + * process in which colors are transformed from an input color + * space to CS_CIEXYZ and then to an output color space. The color + * values returned by this method are not those that would produce + * the XYZ value passed to the method when measured by a colorimeter. + * If you have XYZ values corresponding to measurements made using + * current CIE recommended practices, they must be converted to D50 + * relative values before being passed to this method. + * The paragraphs below explain this in more detail. + *

+ * The ICC standard uses a device independent color space (DICS) as the + * mechanism for converting color from one device to another device. In + * this architecture, colors are converted from the source device's color + * space to the ICC DICS and then from the ICC DICS to the destination + * device's color space. The ICC standard defines device profiles which + * contain transforms which will convert between a device's color space + * and the ICC DICS. The overall conversion of colors from a source + * device to colors of a destination device is done by connecting the + * device-to-DICS transform of the profile for the source device to the + * DICS-to-device transform of the profile for the destination device. + * For this reason, the ICC DICS is commonly referred to as the profile + * connection space (PCS). The color space used in the methods + * toCIEXYZ and fromCIEXYZ is the CIEXYZ PCS defined by the ICC + * Specification. This is also the color space represented by + * ColorSpace.CS_CIEXYZ. + *

+ * The XYZ values of a color are often represented as relative to some + * white point, so the actual meaning of the XYZ values cannot be known + * without knowing the white point of those values. This is known as + * relative colorimetry. The PCS uses a white point of D50, so the XYZ + * values of the PCS are relative to D50. For example, white in the PCS + * will have the XYZ values of D50, which is defined to be X=.9642, + * Y=1.000, and Z=0.8249. This white point is commonly used for graphic + * arts applications, but others are often used in other applications. + *

+ * To quantify the color characteristics of a device such as a printer + * or monitor, measurements of XYZ values for particular device colors + * are typically made. For purposes of this discussion, the term + * device XYZ values is used to mean the XYZ values that would be + * measured from device colors using current CIE recommended practices. + *

+ * Converting between device XYZ values and the PCS XYZ values taken as + * arguments by this method corresponds to converting between the device's + * color space, as represented by CIE colorimetric values, and the PCS. + * There are many factors involved in this process, some of which are quite + * subtle. The most important, however, is the adjustment made to account + * for differences between the device's white point and the white point of + * the PCS. There are many techniques for doing this and it is the + * subject of much current research and controversy. Some commonly used + * methods are XYZ scaling, the von Kries transform, and the Bradford + * transform. The proper method to use depends upon each particular + * application. + *

+ * The simplest method is XYZ scaling. In this method each device XYZ + * value is converted to a PCS XYZ value by multiplying it by the ratio + * of the PCS white point (D50) to the device white point. + *

+     *
+     * Xd, Yd, Zd are the device XYZ values
+     * Xdw, Ydw, Zdw are the device XYZ white point values
+     * Xp, Yp, Zp are the PCS XYZ values
+     * Xd50, Yd50, Zd50 are the PCS XYZ white point values
+     *
+     * Xp = Xd * (Xd50 / Xdw)
+     * Yp = Yd * (Yd50 / Ydw)
+     * Zp = Zd * (Zd50 / Zdw)
+     *
+     * 
+ *

+ * Conversion from the PCS to the device would be done by inverting these + * equations: + *

+     *
+     * Xd = Xp * (Xdw / Xd50)
+     * Yd = Yp * (Ydw / Yd50)
+     * Zd = Zp * (Zdw / Zd50)
+     *
+     * 
+ *

+ * Note that the media white point tag in an ICC profile is not the same + * as the device white point. The media white point tag is expressed in + * PCS values and is used to represent the difference between the XYZ of + * device illuminant and the XYZ of the device media when measured under + * that illuminant. The device white point is expressed as the device + * XYZ values corresponding to white displayed on the device. For + * example, displaying the RGB color (1.0, 1.0, 1.0) on an sRGB device + * will result in a measured device XYZ value of D65. This will not + * be the same as the media white point tag XYZ value in the ICC + * profile for an sRGB device. + *

+ *

+ * @param colorvalue a float array with length of at least 3. + * @return a float array with length equal to the number of + * components in this ColorSpace. + * @throws ArrayIndexOutOfBoundsException if array length is not + * at least 3. + */ + public float[] fromCIEXYZ(float[] colorvalue) { + + if (xyz2this == null) { + ColorTransform[] transformList = new ColorTransform [2]; + ICC_ColorSpace xyzCS = + (ICC_ColorSpace) ColorSpace.getInstance (CS_CIEXYZ); + PCMM mdl = CMSManager.getModule(); + transformList[0] = mdl.createTransform ( + xyzCS.getProfile(), ColorTransform.Any, ColorTransform.In); + try { + transformList[1] = mdl.createTransform( + thisProfile, ICC_Profile.icRelativeColorimetric, + ColorTransform.Out); + } catch (CMMException e) { + transformList[1] = CMSManager.getModule().createTransform( + thisProfile, ColorTransform.Any, ColorTransform.Out); + } + xyz2this = mdl.createTransform(transformList); + if (needScaleInit) { + setComponentScaling(); + } + } + + short tmp[] = new short[3]; + float ALMOST_TWO = 1.0f + (32767.0f / 32768.0f); + float factor = 65535.0f / ALMOST_TWO; + // For CIEXYZ, min = 0.0, max = ALMOST_TWO for all components + for (int i = 0; i < 3; i++) { + tmp[i] = (short) ((colorvalue[i] * factor) + 0.5f); + } + tmp = xyz2this.colorConvert(tmp, null); + int nc = this.getNumComponents(); + float[] result = new float [nc]; + for (int i = 0; i < nc; i++) { + result[i] = (((float) (tmp[i] & 0xffff)) / 65535.0f) * + diffMinMax[i] + minVal[i]; + } + return result; + } + + /** + * Returns the minimum normalized color component value for the + * specified component. For TYPE_XYZ spaces, this method returns + * minimum values of 0.0 for all components. For TYPE_Lab spaces, + * this method returns 0.0 for L and -128.0 for a and b components. + * This is consistent with the encoding of the XYZ and Lab Profile + * Connection Spaces in the ICC specification. For all other types, this + * method returns 0.0 for all components. When using an ICC_ColorSpace + * with a profile that requires different minimum component values, + * it is necessary to subclass this class and override this method. + * @param component The component index. + * @return The minimum normalized component value. + * @throws IllegalArgumentException if component is less than 0 or + * greater than numComponents - 1. + * @since 1.4 + */ + public float getMinValue(int component) { + if ((component < 0) || (component > this.getNumComponents() - 1)) { + throw new IllegalArgumentException( + "Component index out of range: + component"); + } + return minVal[component]; + } + + /** + * Returns the maximum normalized color component value for the + * specified component. For TYPE_XYZ spaces, this method returns + * maximum values of 1.0 + (32767.0 / 32768.0) for all components. + * For TYPE_Lab spaces, + * this method returns 100.0 for L and 127.0 for a and b components. + * This is consistent with the encoding of the XYZ and Lab Profile + * Connection Spaces in the ICC specification. For all other types, this + * method returns 1.0 for all components. When using an ICC_ColorSpace + * with a profile that requires different maximum component values, + * it is necessary to subclass this class and override this method. + * @param component The component index. + * @return The maximum normalized component value. + * @throws IllegalArgumentException if component is less than 0 or + * greater than numComponents - 1. + * @since 1.4 + */ + public float getMaxValue(int component) { + if ((component < 0) || (component > this.getNumComponents() - 1)) { + throw new IllegalArgumentException( + "Component index out of range: + component"); + } + return maxVal[component]; + } + + private void setMinMax() { + int nc = this.getNumComponents(); + int type = this.getType(); + minVal = new float[nc]; + maxVal = new float[nc]; + if (type == ColorSpace.TYPE_Lab) { + minVal[0] = 0.0f; // L + maxVal[0] = 100.0f; + minVal[1] = -128.0f; // a + maxVal[1] = 127.0f; + minVal[2] = -128.0f; // b + maxVal[2] = 127.0f; + } else if (type == ColorSpace.TYPE_XYZ) { + minVal[0] = minVal[1] = minVal[2] = 0.0f; // X, Y, Z + maxVal[0] = maxVal[1] = maxVal[2] = 1.0f + (32767.0f/ 32768.0f); + } else { + for (int i = 0; i < nc; i++) { + minVal[i] = 0.0f; + maxVal[i] = 1.0f; + } + } + } + + private void setComponentScaling() { + int nc = this.getNumComponents(); + diffMinMax = new float[nc]; + invDiffMinMax = new float[nc]; + for (int i = 0; i < nc; i++) { + minVal[i] = this.getMinValue(i); // in case getMinVal is overridden + maxVal[i] = this.getMaxValue(i); // in case getMaxVal is overridden + diffMinMax[i] = maxVal[i] - minVal[i]; + invDiffMinMax[i] = 65535.0f / diffMinMax[i]; + } + needScaleInit = false; + } + +} diff --git a/jdk/src/share/classes/java/awt/color/ICC_Profile.java b/jdk/src/share/classes/java/awt/color/ICC_Profile.java new file mode 100644 index 00000000000..33910a73b78 --- /dev/null +++ b/jdk/src/share/classes/java/awt/color/ICC_Profile.java @@ -0,0 +1,2003 @@ +/* + * Portions Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/********************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.color; + +import sun.java2d.cmm.PCMM; +import sun.java2d.cmm.CMSManager; +import sun.java2d.cmm.ProfileDeferralMgr; +import sun.java2d.cmm.ProfileDeferralInfo; +import sun.java2d.cmm.ProfileActivator; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.OutputStream; +import java.io.Serializable; + +import java.util.StringTokenizer; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * A representation of color profile data for device independent and + * device dependent color spaces based on the International Color + * Consortium Specification ICC.1:2001-12, File Format for Color Profiles, + * (see http://www.color.org). + *

+ * An ICC_ColorSpace object can be constructed from an appropriate + * ICC_Profile. + * Typically, an ICC_ColorSpace would be associated with an ICC + * Profile which is either an input, display, or output profile (see + * the ICC specification). There are also device link, abstract, + * color space conversion, and named color profiles. These are less + * useful for tagging a color or image, but are useful for other + * purposes (in particular device link profiles can provide improved + * performance for converting from one device's color space to + * another's). + *

+ * ICC Profiles represent transformations from the color space of + * the profile (e.g. a monitor) to a Profile Connection Space (PCS). + * Profiles of interest for tagging images or colors have a PCS + * which is one of the two specific device independent + * spaces (one CIEXYZ space and one CIELab space) defined in the + * ICC Profile Format Specification. Most profiles of interest + * either have invertible transformations or explicitly specify + * transformations going both directions. + *

+ * @see ICC_ColorSpace + */ + + +public class ICC_Profile implements Serializable { + + private static final long serialVersionUID = -3938515861990936766L; + + transient long ID; + + private transient ProfileDeferralInfo deferralInfo; + private transient ProfileActivator profileActivator; + + // Registry of singleton profile objects for specific color spaces + // defined in the ColorSpace class (e.g. CS_sRGB), see + // getInstance(int cspace) factory method. + private static ICC_Profile sRGBprofile; + private static ICC_Profile XYZprofile; + private static ICC_Profile PYCCprofile; + private static ICC_Profile GRAYprofile; + private static ICC_Profile LINEAR_RGBprofile; + + + /** + * Profile class is input. + */ + public static final int CLASS_INPUT = 0; + + /** + * Profile class is display. + */ + public static final int CLASS_DISPLAY = 1; + + /** + * Profile class is output. + */ + public static final int CLASS_OUTPUT = 2; + + /** + * Profile class is device link. + */ + public static final int CLASS_DEVICELINK = 3; + + /** + * Profile class is color space conversion. + */ + public static final int CLASS_COLORSPACECONVERSION = 4; + + /** + * Profile class is abstract. + */ + public static final int CLASS_ABSTRACT = 5; + + /** + * Profile class is named color. + */ + public static final int CLASS_NAMEDCOLOR = 6; + + + /** + * ICC Profile Color Space Type Signature: 'XYZ '. + */ + public static final int icSigXYZData = 0x58595A20; /* 'XYZ ' */ + + /** + * ICC Profile Color Space Type Signature: 'Lab '. + */ + public static final int icSigLabData = 0x4C616220; /* 'Lab ' */ + + /** + * ICC Profile Color Space Type Signature: 'Luv '. + */ + public static final int icSigLuvData = 0x4C757620; /* 'Luv ' */ + + /** + * ICC Profile Color Space Type Signature: 'YCbr'. + */ + public static final int icSigYCbCrData = 0x59436272; /* 'YCbr' */ + + /** + * ICC Profile Color Space Type Signature: 'Yxy '. + */ + public static final int icSigYxyData = 0x59787920; /* 'Yxy ' */ + + /** + * ICC Profile Color Space Type Signature: 'RGB '. + */ + public static final int icSigRgbData = 0x52474220; /* 'RGB ' */ + + /** + * ICC Profile Color Space Type Signature: 'GRAY'. + */ + public static final int icSigGrayData = 0x47524159; /* 'GRAY' */ + + /** + * ICC Profile Color Space Type Signature: 'HSV'. + */ + public static final int icSigHsvData = 0x48535620; /* 'HSV ' */ + + /** + * ICC Profile Color Space Type Signature: 'HLS'. + */ + public static final int icSigHlsData = 0x484C5320; /* 'HLS ' */ + + /** + * ICC Profile Color Space Type Signature: 'CMYK'. + */ + public static final int icSigCmykData = 0x434D594B; /* 'CMYK' */ + + /** + * ICC Profile Color Space Type Signature: 'CMY '. + */ + public static final int icSigCmyData = 0x434D5920; /* 'CMY ' */ + + /** + * ICC Profile Color Space Type Signature: '2CLR'. + */ + public static final int icSigSpace2CLR = 0x32434C52; /* '2CLR' */ + + /** + * ICC Profile Color Space Type Signature: '3CLR'. + */ + public static final int icSigSpace3CLR = 0x33434C52; /* '3CLR' */ + + /** + * ICC Profile Color Space Type Signature: '4CLR'. + */ + public static final int icSigSpace4CLR = 0x34434C52; /* '4CLR' */ + + /** + * ICC Profile Color Space Type Signature: '5CLR'. + */ + public static final int icSigSpace5CLR = 0x35434C52; /* '5CLR' */ + + /** + * ICC Profile Color Space Type Signature: '6CLR'. + */ + public static final int icSigSpace6CLR = 0x36434C52; /* '6CLR' */ + + /** + * ICC Profile Color Space Type Signature: '7CLR'. + */ + public static final int icSigSpace7CLR = 0x37434C52; /* '7CLR' */ + + /** + * ICC Profile Color Space Type Signature: '8CLR'. + */ + public static final int icSigSpace8CLR = 0x38434C52; /* '8CLR' */ + + /** + * ICC Profile Color Space Type Signature: '9CLR'. + */ + public static final int icSigSpace9CLR = 0x39434C52; /* '9CLR' */ + + /** + * ICC Profile Color Space Type Signature: 'ACLR'. + */ + public static final int icSigSpaceACLR = 0x41434C52; /* 'ACLR' */ + + /** + * ICC Profile Color Space Type Signature: 'BCLR'. + */ + public static final int icSigSpaceBCLR = 0x42434C52; /* 'BCLR' */ + + /** + * ICC Profile Color Space Type Signature: 'CCLR'. + */ + public static final int icSigSpaceCCLR = 0x43434C52; /* 'CCLR' */ + + /** + * ICC Profile Color Space Type Signature: 'DCLR'. + */ + public static final int icSigSpaceDCLR = 0x44434C52; /* 'DCLR' */ + + /** + * ICC Profile Color Space Type Signature: 'ECLR'. + */ + public static final int icSigSpaceECLR = 0x45434C52; /* 'ECLR' */ + + /** + * ICC Profile Color Space Type Signature: 'FCLR'. + */ + public static final int icSigSpaceFCLR = 0x46434C52; /* 'FCLR' */ + + + /** + * ICC Profile Class Signature: 'scnr'. + */ + public static final int icSigInputClass = 0x73636E72; /* 'scnr' */ + + /** + * ICC Profile Class Signature: 'mntr'. + */ + public static final int icSigDisplayClass = 0x6D6E7472; /* 'mntr' */ + + /** + * ICC Profile Class Signature: 'prtr'. + */ + public static final int icSigOutputClass = 0x70727472; /* 'prtr' */ + + /** + * ICC Profile Class Signature: 'link'. + */ + public static final int icSigLinkClass = 0x6C696E6B; /* 'link' */ + + /** + * ICC Profile Class Signature: 'abst'. + */ + public static final int icSigAbstractClass = 0x61627374; /* 'abst' */ + + /** + * ICC Profile Class Signature: 'spac'. + */ + public static final int icSigColorSpaceClass = 0x73706163; /* 'spac' */ + + /** + * ICC Profile Class Signature: 'nmcl'. + */ + public static final int icSigNamedColorClass = 0x6e6d636c; /* 'nmcl' */ + + + /** + * ICC Profile Rendering Intent: Perceptual. + */ + public static final int icPerceptual = 0; + + /** + * ICC Profile Rendering Intent: RelativeColorimetric. + */ + public static final int icRelativeColorimetric = 1; + + /** + * ICC Profile Rendering Intent: Media-RelativeColorimetric. + * @since 1.5 + */ + public static final int icMediaRelativeColorimetric = 1; + + /** + * ICC Profile Rendering Intent: Saturation. + */ + public static final int icSaturation = 2; + + /** + * ICC Profile Rendering Intent: AbsoluteColorimetric. + */ + public static final int icAbsoluteColorimetric = 3; + + /** + * ICC Profile Rendering Intent: ICC-AbsoluteColorimetric. + * @since 1.5 + */ + public static final int icICCAbsoluteColorimetric = 3; + + + /** + * ICC Profile Tag Signature: 'head' - special. + */ + public static final int icSigHead = 0x68656164; /* 'head' - special */ + + /** + * ICC Profile Tag Signature: 'A2B0'. + */ + public static final int icSigAToB0Tag = 0x41324230; /* 'A2B0' */ + + /** + * ICC Profile Tag Signature: 'A2B1'. + */ + public static final int icSigAToB1Tag = 0x41324231; /* 'A2B1' */ + + /** + * ICC Profile Tag Signature: 'A2B2'. + */ + public static final int icSigAToB2Tag = 0x41324232; /* 'A2B2' */ + + /** + * ICC Profile Tag Signature: 'bXYZ'. + */ + public static final int icSigBlueColorantTag = 0x6258595A; /* 'bXYZ' */ + + /** + * ICC Profile Tag Signature: 'bXYZ'. + * @since 1.5 + */ + public static final int icSigBlueMatrixColumnTag = 0x6258595A; /* 'bXYZ' */ + + /** + * ICC Profile Tag Signature: 'bTRC'. + */ + public static final int icSigBlueTRCTag = 0x62545243; /* 'bTRC' */ + + /** + * ICC Profile Tag Signature: 'B2A0'. + */ + public static final int icSigBToA0Tag = 0x42324130; /* 'B2A0' */ + + /** + * ICC Profile Tag Signature: 'B2A1'. + */ + public static final int icSigBToA1Tag = 0x42324131; /* 'B2A1' */ + + /** + * ICC Profile Tag Signature: 'B2A2'. + */ + public static final int icSigBToA2Tag = 0x42324132; /* 'B2A2' */ + + /** + * ICC Profile Tag Signature: 'calt'. + */ + public static final int icSigCalibrationDateTimeTag = 0x63616C74; + /* 'calt' */ + + /** + * ICC Profile Tag Signature: 'targ'. + */ + public static final int icSigCharTargetTag = 0x74617267; /* 'targ' */ + + /** + * ICC Profile Tag Signature: 'cprt'. + */ + public static final int icSigCopyrightTag = 0x63707274; /* 'cprt' */ + + /** + * ICC Profile Tag Signature: 'crdi'. + */ + public static final int icSigCrdInfoTag = 0x63726469; /* 'crdi' */ + + /** + * ICC Profile Tag Signature: 'dmnd'. + */ + public static final int icSigDeviceMfgDescTag = 0x646D6E64; /* 'dmnd' */ + + /** + * ICC Profile Tag Signature: 'dmdd'. + */ + public static final int icSigDeviceModelDescTag = 0x646D6464; /* 'dmdd' */ + + /** + * ICC Profile Tag Signature: 'devs'. + */ + public static final int icSigDeviceSettingsTag = 0x64657673; /* 'devs' */ + + /** + * ICC Profile Tag Signature: 'gamt'. + */ + public static final int icSigGamutTag = 0x67616D74; /* 'gamt' */ + + /** + * ICC Profile Tag Signature: 'kTRC'. + */ + public static final int icSigGrayTRCTag = 0x6b545243; /* 'kTRC' */ + + /** + * ICC Profile Tag Signature: 'gXYZ'. + */ + public static final int icSigGreenColorantTag = 0x6758595A; /* 'gXYZ' */ + + /** + * ICC Profile Tag Signature: 'gXYZ'. + * @since 1.5 + */ + public static final int icSigGreenMatrixColumnTag = 0x6758595A;/* 'gXYZ' */ + + /** + * ICC Profile Tag Signature: 'gTRC'. + */ + public static final int icSigGreenTRCTag = 0x67545243; /* 'gTRC' */ + + /** + * ICC Profile Tag Signature: 'lumi'. + */ + public static final int icSigLuminanceTag = 0x6C756d69; /* 'lumi' */ + + /** + * ICC Profile Tag Signature: 'meas'. + */ + public static final int icSigMeasurementTag = 0x6D656173; /* 'meas' */ + + /** + * ICC Profile Tag Signature: 'bkpt'. + */ + public static final int icSigMediaBlackPointTag = 0x626B7074; /* 'bkpt' */ + + /** + * ICC Profile Tag Signature: 'wtpt'. + */ + public static final int icSigMediaWhitePointTag = 0x77747074; /* 'wtpt' */ + + /** + * ICC Profile Tag Signature: 'ncl2'. + */ + public static final int icSigNamedColor2Tag = 0x6E636C32; /* 'ncl2' */ + + /** + * ICC Profile Tag Signature: 'resp'. + */ + public static final int icSigOutputResponseTag = 0x72657370; /* 'resp' */ + + /** + * ICC Profile Tag Signature: 'pre0'. + */ + public static final int icSigPreview0Tag = 0x70726530; /* 'pre0' */ + + /** + * ICC Profile Tag Signature: 'pre1'. + */ + public static final int icSigPreview1Tag = 0x70726531; /* 'pre1' */ + + /** + * ICC Profile Tag Signature: 'pre2'. + */ + public static final int icSigPreview2Tag = 0x70726532; /* 'pre2' */ + + /** + * ICC Profile Tag Signature: 'desc'. + */ + public static final int icSigProfileDescriptionTag = 0x64657363; + /* 'desc' */ + + /** + * ICC Profile Tag Signature: 'pseq'. + */ + public static final int icSigProfileSequenceDescTag = 0x70736571; + /* 'pseq' */ + + /** + * ICC Profile Tag Signature: 'psd0'. + */ + public static final int icSigPs2CRD0Tag = 0x70736430; /* 'psd0' */ + + /** + * ICC Profile Tag Signature: 'psd1'. + */ + public static final int icSigPs2CRD1Tag = 0x70736431; /* 'psd1' */ + + /** + * ICC Profile Tag Signature: 'psd2'. + */ + public static final int icSigPs2CRD2Tag = 0x70736432; /* 'psd2' */ + + /** + * ICC Profile Tag Signature: 'psd3'. + */ + public static final int icSigPs2CRD3Tag = 0x70736433; /* 'psd3' */ + + /** + * ICC Profile Tag Signature: 'ps2s'. + */ + public static final int icSigPs2CSATag = 0x70733273; /* 'ps2s' */ + + /** + * ICC Profile Tag Signature: 'ps2i'. + */ + public static final int icSigPs2RenderingIntentTag = 0x70733269; + /* 'ps2i' */ + + /** + * ICC Profile Tag Signature: 'rXYZ'. + */ + public static final int icSigRedColorantTag = 0x7258595A; /* 'rXYZ' */ + + /** + * ICC Profile Tag Signature: 'rXYZ'. + * @since 1.5 + */ + public static final int icSigRedMatrixColumnTag = 0x7258595A; /* 'rXYZ' */ + + /** + * ICC Profile Tag Signature: 'rTRC'. + */ + public static final int icSigRedTRCTag = 0x72545243; /* 'rTRC' */ + + /** + * ICC Profile Tag Signature: 'scrd'. + */ + public static final int icSigScreeningDescTag = 0x73637264; /* 'scrd' */ + + /** + * ICC Profile Tag Signature: 'scrn'. + */ + public static final int icSigScreeningTag = 0x7363726E; /* 'scrn' */ + + /** + * ICC Profile Tag Signature: 'tech'. + */ + public static final int icSigTechnologyTag = 0x74656368; /* 'tech' */ + + /** + * ICC Profile Tag Signature: 'bfd '. + */ + public static final int icSigUcrBgTag = 0x62666420; /* 'bfd ' */ + + /** + * ICC Profile Tag Signature: 'vued'. + */ + public static final int icSigViewingCondDescTag = 0x76756564; /* 'vued' */ + + /** + * ICC Profile Tag Signature: 'view'. + */ + public static final int icSigViewingConditionsTag = 0x76696577;/* 'view' */ + + /** + * ICC Profile Tag Signature: 'chrm'. + */ + public static final int icSigChromaticityTag = 0x6368726d; /* 'chrm' */ + + /** + * ICC Profile Tag Signature: 'chad'. + * @since 1.5 + */ + public static final int icSigChromaticAdaptationTag = 0x63686164;/* 'chad' */ + + /** + * ICC Profile Tag Signature: 'clro'. + * @since 1.5 + */ + public static final int icSigColorantOrderTag = 0x636C726F; /* 'clro' */ + + /** + * ICC Profile Tag Signature: 'clrt'. + * @since 1.5 + */ + public static final int icSigColorantTableTag = 0x636C7274; /* 'clrt' */ + + + /** + * ICC Profile Header Location: profile size in bytes. + */ + public static final int icHdrSize = 0; /* Profile size in bytes */ + + /** + * ICC Profile Header Location: CMM for this profile. + */ + public static final int icHdrCmmId = 4; /* CMM for this profile */ + + /** + * ICC Profile Header Location: format version number. + */ + public static final int icHdrVersion = 8; /* Format version number */ + + /** + * ICC Profile Header Location: type of profile. + */ + public static final int icHdrDeviceClass = 12; /* Type of profile */ + + /** + * ICC Profile Header Location: color space of data. + */ + public static final int icHdrColorSpace = 16; /* Color space of data */ + + /** + * ICC Profile Header Location: PCS - XYZ or Lab only. + */ + public static final int icHdrPcs = 20; /* PCS - XYZ or Lab only */ + + /** + * ICC Profile Header Location: date profile was created. + */ + public static final int icHdrDate = 24; /* Date profile was created */ + + /** + * ICC Profile Header Location: icMagicNumber. + */ + public static final int icHdrMagic = 36; /* icMagicNumber */ + + /** + * ICC Profile Header Location: primary platform. + */ + public static final int icHdrPlatform = 40; /* Primary Platform */ + + /** + * ICC Profile Header Location: various bit settings. + */ + public static final int icHdrFlags = 44; /* Various bit settings */ + + /** + * ICC Profile Header Location: device manufacturer. + */ + public static final int icHdrManufacturer = 48; /* Device manufacturer */ + + /** + * ICC Profile Header Location: device model number. + */ + public static final int icHdrModel = 52; /* Device model number */ + + /** + * ICC Profile Header Location: device attributes. + */ + public static final int icHdrAttributes = 56; /* Device attributes */ + + /** + * ICC Profile Header Location: rendering intent. + */ + public static final int icHdrRenderingIntent = 64; /* Rendering intent */ + + /** + * ICC Profile Header Location: profile illuminant. + */ + public static final int icHdrIlluminant = 68; /* Profile illuminant */ + + /** + * ICC Profile Header Location: profile creator. + */ + public static final int icHdrCreator = 80; /* Profile creator */ + + /** + * ICC Profile Header Location: profile's ID. + * @since 1.5 + */ + public static final int icHdrProfileID = 84; /* Profile's ID */ + + + /** + * ICC Profile Constant: tag type signaturE. + */ + public static final int icTagType = 0; /* tag type signature */ + + /** + * ICC Profile Constant: reserved. + */ + public static final int icTagReserved = 4; /* reserved */ + + /** + * ICC Profile Constant: curveType count. + */ + public static final int icCurveCount = 8; /* curveType count */ + + /** + * ICC Profile Constant: curveType data. + */ + public static final int icCurveData = 12; /* curveType data */ + + /** + * ICC Profile Constant: XYZNumber X. + */ + public static final int icXYZNumberX = 8; /* XYZNumber X */ + + + /** + * Constructs an ICC_Profile object with a given ID. + */ + ICC_Profile(long ID) { + this.ID = ID; + } + + + /** + * Constructs an ICC_Profile object whose loading will be deferred. + * The ID will be 0 until the profile is loaded. + */ + ICC_Profile(ProfileDeferralInfo pdi) { + this.deferralInfo = pdi; + this.profileActivator = new ProfileActivator() { + public void activate() { + activateDeferredProfile(); + } + }; + ProfileDeferralMgr.registerDeferral(this.profileActivator); + } + + + /** + * Frees the resources associated with an ICC_Profile object. + */ + protected void finalize () { + if (ID != 0) { + CMSManager.getModule().freeProfile(ID); + } else if (profileActivator != null) { + ProfileDeferralMgr.unregisterDeferral(profileActivator); + } + } + + + /** + * Constructs an ICC_Profile object corresponding to the data in + * a byte array. Throws an IllegalArgumentException if the data + * does not correspond to a valid ICC Profile. + * @param data the specified ICC Profile data + * @return an ICC_Profile object corresponding to + * the data in the specified data array. + */ + public static ICC_Profile getInstance(byte[] data) { + ICC_Profile thisProfile; + + long theID; + + if (ProfileDeferralMgr.deferring) { + ProfileDeferralMgr.activateProfiles(); + } + + try { + theID = CMSManager.getModule().loadProfile(data); + } catch (CMMException c) { + throw new IllegalArgumentException("Invalid ICC Profile Data"); + } + + try { + if ((getColorSpaceType (theID) == ColorSpace.TYPE_GRAY) && + (getData (theID, icSigMediaWhitePointTag) != null) && + (getData (theID, icSigGrayTRCTag) != null)) { + thisProfile = new ICC_ProfileGray (theID); + } + else if ((getColorSpaceType (theID) == ColorSpace.TYPE_RGB) && + (getData (theID, icSigMediaWhitePointTag) != null) && + (getData (theID, icSigRedColorantTag) != null) && + (getData (theID, icSigGreenColorantTag) != null) && + (getData (theID, icSigBlueColorantTag) != null) && + (getData (theID, icSigRedTRCTag) != null) && + (getData (theID, icSigGreenTRCTag) != null) && + (getData (theID, icSigBlueTRCTag) != null)) { + thisProfile = new ICC_ProfileRGB (theID); + } + else { + thisProfile = new ICC_Profile (theID); + } + } catch (CMMException c) { + thisProfile = new ICC_Profile (theID); + } + return thisProfile; + } + + + + /** + * Constructs an ICC_Profile corresponding to one of the specific color + * spaces defined by the ColorSpace class (for example CS_sRGB). + * Throws an IllegalArgumentException if cspace is not one of the + * defined color spaces. + * + * @param cspace the type of color space to create a profile for. + * The specified type is one of the color + * space constants defined in the ColorSpace class. + * + * @return an ICC_Profile object corresponding to + * the specified ColorSpace type. + * @exception IllegalArgumentException If cspace is not + * one of the predefined color space types. + */ + public static ICC_Profile getInstance (int cspace) { + ICC_Profile thisProfile = null; + String fileName; + + switch (cspace) { + case ColorSpace.CS_sRGB: + synchronized(ICC_Profile.class) { + if (sRGBprofile == null) { + try { + /* + * Deferral is only used for standard profiles. + * Enabling the appropriate access privileges is handled + * at a lower level. + */ + sRGBprofile = getDeferredInstance( + new ProfileDeferralInfo("sRGB.pf", + ColorSpace.TYPE_RGB, + 3, CLASS_DISPLAY)); + } catch (IOException e) { + throw new IllegalArgumentException( + "Can't load standard profile: sRGB.pf"); + } + } + thisProfile = sRGBprofile; + } + + break; + + case ColorSpace.CS_CIEXYZ: + synchronized(ICC_Profile.class) { + if (XYZprofile == null) { + XYZprofile = getStandardProfile("CIEXYZ.pf"); + } + thisProfile = XYZprofile; + } + + break; + + case ColorSpace.CS_PYCC: + synchronized(ICC_Profile.class) { + if (PYCCprofile == null) { + PYCCprofile = getStandardProfile("PYCC.pf"); + } + thisProfile = PYCCprofile; + } + + break; + + case ColorSpace.CS_GRAY: + synchronized(ICC_Profile.class) { + if (GRAYprofile == null) { + GRAYprofile = getStandardProfile("GRAY.pf"); + } + thisProfile = GRAYprofile; + } + + break; + + case ColorSpace.CS_LINEAR_RGB: + synchronized(ICC_Profile.class) { + if (LINEAR_RGBprofile == null) { + LINEAR_RGBprofile = getStandardProfile("LINEAR_RGB.pf"); + } + thisProfile = LINEAR_RGBprofile; + } + + break; + + default: + throw new IllegalArgumentException("Unknown color space"); + } + + return thisProfile; + } + + /* This asserts system privileges, so is used only for the + * standard profiles. + */ + private static ICC_Profile getStandardProfile(final String name) { + + return (ICC_Profile) AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + ICC_Profile p = null; + try { + p = getInstance (name); + } catch (IOException ex) { + throw new IllegalArgumentException( + "Can't load standard profile: " + name); + } + return p; + } + }); + } + + /** + * Constructs an ICC_Profile corresponding to the data in a file. + * fileName may be an absolute or a relative file specification. + * Relative file names are looked for in several places: first, relative + * to any directories specified by the java.iccprofile.path property; + * second, relative to any directories specified by the java.class.path + * property; finally, in a directory used to store profiles always + * available, such as the profile for sRGB. Built-in profiles use .pf as + * the file name extension for profiles, e.g. sRGB.pf. + * This method throws an IOException if the specified file cannot be + * opened or if an I/O error occurs while reading the file. It throws + * an IllegalArgumentException if the file does not contain valid ICC + * Profile data. + * @param fileName The file that contains the data for the profile. + * + * @return an ICC_Profile object corresponding to + * the data in the specified file. + * @exception IOException If the specified file cannot be opened or + * an I/O error occurs while reading the file. + * + * @exception IllegalArgumentException If the file does not + * contain valid ICC Profile data. + * + * @exception SecurityException If a security manager is installed + * and it does not permit read access to the given file. + */ + public static ICC_Profile getInstance(String fileName) throws IOException { + ICC_Profile thisProfile; + FileInputStream fis; + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(fileName); + } + + if ((fis = openProfile(fileName)) == null) { + throw new IOException("Cannot open file " + fileName); + } + + thisProfile = getInstance(fis); + + fis.close(); /* close the file */ + + return thisProfile; + } + + + /** + * Constructs an ICC_Profile corresponding to the data in an InputStream. + * This method throws an IllegalArgumentException if the stream does not + * contain valid ICC Profile data. It throws an IOException if an I/O + * error occurs while reading the stream. + * @param s The input stream from which to read the profile data. + * + * @return an ICC_Profile object corresponding to the + * data in the specified InputStream. + * + * @exception IOException If an I/O error occurs while reading the stream. + * + * @exception IllegalArgumentException If the stream does not + * contain valid ICC Profile data. + */ + public static ICC_Profile getInstance(InputStream s) throws IOException { + byte profileData[]; + + if (s instanceof ProfileDeferralInfo) { + /* hack to detect profiles whose loading can be deferred */ + return getDeferredInstance((ProfileDeferralInfo) s); + } + + if ((profileData = getProfileDataFromStream(s)) == null) { + throw new IllegalArgumentException("Invalid ICC Profile Data"); + } + + return getInstance(profileData); + } + + + static byte[] getProfileDataFromStream(InputStream s) throws IOException { + byte profileData[]; + int profileSize; + + byte header[] = new byte[128]; + int bytestoread = 128; + int bytesread = 0; + int n; + + while (bytestoread != 0) { + if ((n = s.read(header, bytesread, bytestoread)) < 0) { + return null; + } + bytesread += n; + bytestoread -= n; + } + if (header[36] != 0x61 || header[37] != 0x63 || + header[38] != 0x73 || header[39] != 0x70) { + return null; /* not a valid profile */ + } + profileSize = ((header[0] & 0xff) << 24) | + ((header[1] & 0xff) << 16) | + ((header[2] & 0xff) << 8) | + (header[3] & 0xff); + profileData = new byte[profileSize]; + System.arraycopy(header, 0, profileData, 0, 128); + bytestoread = profileSize - 128; + bytesread = 128; + while (bytestoread != 0) { + if ((n = s.read(profileData, bytesread, bytestoread)) < 0) { + return null; + } + bytesread += n; + bytestoread -= n; + } + + return profileData; + } + + + /** + * Constructs an ICC_Profile for which the actual loading of the + * profile data from a file and the initialization of the CMM should + * be deferred as long as possible. + * Deferral is only used for standard profiles. + * If deferring is disabled, then getStandardProfile() ensures + * that all of the appropriate access privileges are granted + * when loading this profile. + * If deferring is enabled, then the deferred activation + * code will take care of access privileges. + * @see activateDeferredProfile() + */ + static ICC_Profile getDeferredInstance(ProfileDeferralInfo pdi) + throws IOException { + + if (!ProfileDeferralMgr.deferring) { + return getStandardProfile(pdi.filename); + } + if (pdi.colorSpaceType == ColorSpace.TYPE_RGB) { + return new ICC_ProfileRGB(pdi); + } else if (pdi.colorSpaceType == ColorSpace.TYPE_GRAY) { + return new ICC_ProfileGray(pdi); + } else { + return new ICC_Profile(pdi); + } + } + + + void activateDeferredProfile() { + byte profileData[]; + FileInputStream fis; + String fileName = deferralInfo.filename; + + profileActivator = null; + deferralInfo = null; + if ((fis = openProfile(fileName)) == null) { + throw new IllegalArgumentException("Cannot open file " + fileName); + } + try { + profileData = getProfileDataFromStream(fis); + fis.close(); /* close the file */ + } + catch (IOException e) { + throw new IllegalArgumentException("Invalid ICC Profile Data" + + fileName); + } + if (profileData == null) { + throw new IllegalArgumentException("Invalid ICC Profile Data" + + fileName); + } + try { + ID = CMSManager.getModule().loadProfile(profileData); + } catch (CMMException c) { + throw new IllegalArgumentException("Invalid ICC Profile Data" + + fileName); + } + } + + + /** + * Returns profile major version. + * @return The major version of the profile. + */ + public int getMajorVersion() { + byte[] theHeader; + + theHeader = getData(icSigHead); /* getData will activate deferred + profiles if necessary */ + + return (int) theHeader[8]; + } + + /** + * Returns profile minor version. + * @return The minor version of the profile. + */ + public int getMinorVersion() { + byte[] theHeader; + + theHeader = getData(icSigHead); /* getData will activate deferred + profiles if necessary */ + + return (int) theHeader[9]; + } + + /** + * Returns the profile class. + * @return One of the predefined profile class constants. + */ + public int getProfileClass() { + byte[] theHeader; + int theClassSig, theClass; + + if (deferralInfo != null) { + return deferralInfo.profileClass; /* Need to have this info for + ICC_ColorSpace without + causing a deferred profile + to be loaded */ + } + + theHeader = getData(icSigHead); + + theClassSig = intFromBigEndian (theHeader, icHdrDeviceClass); + + switch (theClassSig) { + case icSigInputClass: + theClass = CLASS_INPUT; + break; + + case icSigDisplayClass: + theClass = CLASS_DISPLAY; + break; + + case icSigOutputClass: + theClass = CLASS_OUTPUT; + break; + + case icSigLinkClass: + theClass = CLASS_DEVICELINK; + break; + + case icSigColorSpaceClass: + theClass = CLASS_COLORSPACECONVERSION; + break; + + case icSigAbstractClass: + theClass = CLASS_ABSTRACT; + break; + + case icSigNamedColorClass: + theClass = CLASS_NAMEDCOLOR; + break; + + default: + throw new IllegalArgumentException("Unknown profile class"); + } + + return theClass; + } + + /** + * Returns the color space type. Returns one of the color space type + * constants defined by the ColorSpace class. This is the + * "input" color space of the profile. The type defines the + * number of components of the color space and the interpretation, + * e.g. TYPE_RGB identifies a color space with three components - red, + * green, and blue. It does not define the particular color + * characteristics of the space, e.g. the chromaticities of the + * primaries. + * @return One of the color space type constants defined in the + * ColorSpace class. + */ + public int getColorSpaceType() { + if (deferralInfo != null) { + return deferralInfo.colorSpaceType; /* Need to have this info for + ICC_ColorSpace without + causing a deferred profile + to be loaded */ + } + return getColorSpaceType(ID); + } + + static int getColorSpaceType(long profileID) { + byte[] theHeader; + int theColorSpaceSig, theColorSpace; + + theHeader = getData(profileID, icSigHead); + theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace); + theColorSpace = iccCStoJCS (theColorSpaceSig); + return theColorSpace; + } + + /** + * Returns the color space type of the Profile Connection Space (PCS). + * Returns one of the color space type constants defined by the + * ColorSpace class. This is the "output" color space of the + * profile. For an input, display, or output profile useful + * for tagging colors or images, this will be either TYPE_XYZ or + * TYPE_Lab and should be interpreted as the corresponding specific + * color space defined in the ICC specification. For a device + * link profile, this could be any of the color space type constants. + * @return One of the color space type constants defined in the + * ColorSpace class. + */ + public int getPCSType() { + if (ProfileDeferralMgr.deferring) { + ProfileDeferralMgr.activateProfiles(); + } + return getPCSType(ID); + } + + + static int getPCSType(long profileID) { + byte[] theHeader; + int thePCSSig, thePCS; + + theHeader = getData(profileID, icSigHead); + thePCSSig = intFromBigEndian(theHeader, icHdrPcs); + thePCS = iccCStoJCS(thePCSSig); + return thePCS; + } + + + /** + * Write this ICC_Profile to a file. + * + * @param fileName The file to write the profile data to. + * + * @exception IOException If the file cannot be opened for writing + * or an I/O error occurs while writing to the file. + */ + public void write(String fileName) throws IOException { + FileOutputStream outputFile; + byte profileData[]; + + profileData = getData(); /* this will activate deferred + profiles if necessary */ + outputFile = new FileOutputStream(fileName); + outputFile.write(profileData); + outputFile.close (); + } + + + /** + * Write this ICC_Profile to an OutputStream. + * + * @param s The stream to write the profile data to. + * + * @exception IOException If an I/O error occurs while writing to the + * stream. + */ + public void write(OutputStream s) throws IOException { + byte profileData[]; + + profileData = getData(); /* this will activate deferred + profiles if necessary */ + s.write(profileData); + } + + + /** + * Returns a byte array corresponding to the data of this ICC_Profile. + * @return A byte array that contains the profile data. + * @see #setData(int, byte[]) + */ + public byte[] getData() { + int profileSize; + byte[] profileData; + + if (ProfileDeferralMgr.deferring) { + ProfileDeferralMgr.activateProfiles(); + } + + PCMM mdl = CMSManager.getModule(); + + /* get the number of bytes needed for this profile */ + profileSize = mdl.getProfileSize(ID); + + profileData = new byte [profileSize]; + + /* get the data for the profile */ + mdl.getProfileData(ID, profileData); + + return profileData; + } + + + /** + * Returns a particular tagged data element from the profile as + * a byte array. Elements are identified by signatures + * as defined in the ICC specification. The signature + * icSigHead can be used to get the header. This method is useful + * for advanced applets or applications which need to access + * profile data directly. + * + * @param tagSignature The ICC tag signature for the data element you + * want to get. + * + * @return A byte array that contains the tagged data element. Returns + * null if the specified tag doesn't exist. + * @see #setData(int, byte[]) + */ + public byte[] getData(int tagSignature) { + + if (ProfileDeferralMgr.deferring) { + ProfileDeferralMgr.activateProfiles(); + } + + return getData(ID, tagSignature); + } + + + static byte[] getData(long profileID, int tagSignature) { + int tagSize; + byte[] tagData; + + try { + PCMM mdl = CMSManager.getModule(); + + /* get the number of bytes needed for this tag */ + tagSize = mdl.getTagSize(profileID, tagSignature); + + tagData = new byte[tagSize]; /* get an array for the tag */ + + /* get the tag's data */ + mdl.getTagData(profileID, tagSignature, tagData); + } catch(CMMException c) { + tagData = null; + } + + return tagData; + } + + /** + * Sets a particular tagged data element in the profile from + * a byte array. This method is useful + * for advanced applets or applications which need to access + * profile data directly. + * + * @param tagSignature The ICC tag signature for the data element + * you want to set. + * @param tagData the data to set for the specified tag signature + * @see #getData + */ + public void setData(int tagSignature, byte[] tagData) { + + if (ProfileDeferralMgr.deferring) { + ProfileDeferralMgr.activateProfiles(); + } + + CMSManager.getModule().setTagData(ID, tagSignature, tagData); + } + + /** + * Sets the rendering intent of the profile. + * This is used to select the proper transform from a profile that + * has multiple transforms. + */ + void setRenderingIntent(int renderingIntent) { + byte[] theHeader = getData(icSigHead);/* getData will activate deferred + profiles if necessary */ + intToBigEndian (renderingIntent, theHeader, icHdrRenderingIntent); + /* set the rendering intent */ + setData (icSigHead, theHeader); + } + + + /** + * Returns the rendering intent of the profile. + * This is used to select the proper transform from a profile that + * has multiple transforms. It is typically set in a source profile + * to select a transform from an output profile. + */ + int getRenderingIntent() { + byte[] theHeader = getData(icSigHead);/* getData will activate deferred + profiles if necessary */ + + int renderingIntent = intFromBigEndian(theHeader, icHdrRenderingIntent); + /* set the rendering intent */ + return renderingIntent; + } + + + /** + * Returns the number of color components in the "input" color + * space of this profile. For example if the color space type + * of this profile is TYPE_RGB, then this method will return 3. + * + * @return The number of color components in the profile's input + * color space. + * + * @throws ProfileDataException if color space is in the profile + * is invalid + */ + public int getNumComponents() { + byte[] theHeader; + int theColorSpaceSig, theNumComponents; + + if (deferralInfo != null) { + return deferralInfo.numComponents; /* Need to have this info for + ICC_ColorSpace without + causing a deferred profile + to be loaded */ + } + theHeader = getData(icSigHead); + + theColorSpaceSig = intFromBigEndian (theHeader, icHdrColorSpace); + + switch (theColorSpaceSig) { + case icSigGrayData: + theNumComponents = 1; + break; + + case icSigSpace2CLR: + theNumComponents = 2; + break; + + case icSigXYZData: + case icSigLabData: + case icSigLuvData: + case icSigYCbCrData: + case icSigYxyData: + case icSigRgbData: + case icSigHsvData: + case icSigHlsData: + case icSigCmyData: + case icSigSpace3CLR: + theNumComponents = 3; + break; + + case icSigCmykData: + case icSigSpace4CLR: + theNumComponents = 4; + break; + + case icSigSpace5CLR: + theNumComponents = 5; + break; + + case icSigSpace6CLR: + theNumComponents = 6; + break; + + case icSigSpace7CLR: + theNumComponents = 7; + break; + + case icSigSpace8CLR: + theNumComponents = 8; + break; + + case icSigSpace9CLR: + theNumComponents = 9; + break; + + case icSigSpaceACLR: + theNumComponents = 10; + break; + + case icSigSpaceBCLR: + theNumComponents = 11; + break; + + case icSigSpaceCCLR: + theNumComponents = 12; + break; + + case icSigSpaceDCLR: + theNumComponents = 13; + break; + + case icSigSpaceECLR: + theNumComponents = 14; + break; + + case icSigSpaceFCLR: + theNumComponents = 15; + break; + + default: + throw new ProfileDataException ("invalid ICC color space"); + } + + return theNumComponents; + } + + + /** + * Returns a float array of length 3 containing the X, Y, and Z + * components of the mediaWhitePointTag in the ICC profile. + */ + float[] getMediaWhitePoint() { + return getXYZTag(icSigMediaWhitePointTag); + /* get the media white point tag */ + } + + + /** + * Returns a float array of length 3 containing the X, Y, and Z + * components encoded in an XYZType tag. + */ + float[] getXYZTag(int theTagSignature) { + byte[] theData; + float[] theXYZNumber; + int i1, i2, theS15Fixed16; + + theData = getData(theTagSignature); /* get the tag data */ + /* getData will activate deferred + profiles if necessary */ + + theXYZNumber = new float [3]; /* array to return */ + + /* convert s15Fixed16Number to float */ + for (i1 = 0, i2 = icXYZNumberX; i1 < 3; i1++, i2 += 4) { + theS15Fixed16 = intFromBigEndian(theData, i2); + theXYZNumber [i1] = ((float) theS15Fixed16) / 65536.0f; + } + return theXYZNumber; + } + + + /** + * Returns a gamma value representing a tone reproduction + * curve (TRC). If the profile represents the TRC as a table rather + * than a single gamma value, then an exception is thrown. In this + * case the actual table can be obtained via getTRC(). + * theTagSignature should be one of icSigGrayTRCTag, icSigRedTRCTag, + * icSigGreenTRCTag, or icSigBlueTRCTag. + * @return the gamma value as a float. + * @exception ProfileDataException if the profile does not specify + * the TRC as a single gamma value. + */ + float getGamma(int theTagSignature) { + byte[] theTRCData; + float theGamma; + int theU8Fixed8; + + theTRCData = getData(theTagSignature); /* get the TRC */ + /* getData will activate deferred + profiles if necessary */ + + if (intFromBigEndian (theTRCData, icCurveCount) != 1) { + throw new ProfileDataException ("TRC is not a gamma"); + } + + /* convert u8Fixed8 to float */ + theU8Fixed8 = (shortFromBigEndian(theTRCData, icCurveData)) & 0xffff; + + theGamma = ((float) theU8Fixed8) / 256.0f; + + return theGamma; + } + + + /** + * Returns the TRC as an array of shorts. If the profile has + * specified the TRC as linear (gamma = 1.0) or as a simple gamma + * value, this method throws an exception, and the getGamma() method + * should be used to get the gamma value. Otherwise the short array + * returned here represents a lookup table where the input Gray value + * is conceptually in the range [0.0, 1.0]. Value 0.0 maps + * to array index 0 and value 1.0 maps to array index length-1. + * Interpolation may be used to generate output values for + * input values which do not map exactly to an index in the + * array. Output values also map linearly to the range [0.0, 1.0]. + * Value 0.0 is represented by an array value of 0x0000 and + * value 1.0 by 0xFFFF, i.e. the values are really unsigned + * short values, although they are returned in a short array. + * theTagSignature should be one of icSigGrayTRCTag, icSigRedTRCTag, + * icSigGreenTRCTag, or icSigBlueTRCTag. + * @return a short array representing the TRC. + * @exception ProfileDataException if the profile does not specify + * the TRC as a table. + */ + short[] getTRC(int theTagSignature) { + byte[] theTRCData; + short[] theTRC; + int i1, i2, nElements, theU8Fixed8; + + theTRCData = getData(theTagSignature); /* get the TRC */ + /* getData will activate deferred + profiles if necessary */ + + nElements = intFromBigEndian(theTRCData, icCurveCount); + + if (nElements == 1) { + throw new ProfileDataException("TRC is not a table"); + } + + /* make the short array */ + theTRC = new short [nElements]; + + for (i1 = 0, i2 = icCurveData; i1 < nElements; i1++, i2 += 2) { + theTRC[i1] = shortFromBigEndian(theTRCData, i2); + } + + return theTRC; + } + + + /* convert an ICC color space signature into a Java color space type */ + static int iccCStoJCS(int theColorSpaceSig) { + int theColorSpace; + + switch (theColorSpaceSig) { + case icSigXYZData: + theColorSpace = ColorSpace.TYPE_XYZ; + break; + + case icSigLabData: + theColorSpace = ColorSpace.TYPE_Lab; + break; + + case icSigLuvData: + theColorSpace = ColorSpace.TYPE_Luv; + break; + + case icSigYCbCrData: + theColorSpace = ColorSpace.TYPE_YCbCr; + break; + + case icSigYxyData: + theColorSpace = ColorSpace.TYPE_Yxy; + break; + + case icSigRgbData: + theColorSpace = ColorSpace.TYPE_RGB; + break; + + case icSigGrayData: + theColorSpace = ColorSpace.TYPE_GRAY; + break; + + case icSigHsvData: + theColorSpace = ColorSpace.TYPE_HSV; + break; + + case icSigHlsData: + theColorSpace = ColorSpace.TYPE_HLS; + break; + + case icSigCmykData: + theColorSpace = ColorSpace.TYPE_CMYK; + break; + + case icSigCmyData: + theColorSpace = ColorSpace.TYPE_CMY; + break; + + case icSigSpace2CLR: + theColorSpace = ColorSpace.TYPE_2CLR; + break; + + case icSigSpace3CLR: + theColorSpace = ColorSpace.TYPE_3CLR; + break; + + case icSigSpace4CLR: + theColorSpace = ColorSpace.TYPE_4CLR; + break; + + case icSigSpace5CLR: + theColorSpace = ColorSpace.TYPE_5CLR; + break; + + case icSigSpace6CLR: + theColorSpace = ColorSpace.TYPE_6CLR; + break; + + case icSigSpace7CLR: + theColorSpace = ColorSpace.TYPE_7CLR; + break; + + case icSigSpace8CLR: + theColorSpace = ColorSpace.TYPE_8CLR; + break; + + case icSigSpace9CLR: + theColorSpace = ColorSpace.TYPE_9CLR; + break; + + case icSigSpaceACLR: + theColorSpace = ColorSpace.TYPE_ACLR; + break; + + case icSigSpaceBCLR: + theColorSpace = ColorSpace.TYPE_BCLR; + break; + + case icSigSpaceCCLR: + theColorSpace = ColorSpace.TYPE_CCLR; + break; + + case icSigSpaceDCLR: + theColorSpace = ColorSpace.TYPE_DCLR; + break; + + case icSigSpaceECLR: + theColorSpace = ColorSpace.TYPE_ECLR; + break; + + case icSigSpaceFCLR: + theColorSpace = ColorSpace.TYPE_FCLR; + break; + + default: + throw new IllegalArgumentException ("Unknown color space"); + } + + return theColorSpace; + } + + + static int intFromBigEndian(byte[] array, int index) { + return (((array[index] & 0xff) << 24) | + ((array[index+1] & 0xff) << 16) | + ((array[index+2] & 0xff) << 8) | + (array[index+3] & 0xff)); + } + + + static void intToBigEndian(int value, byte[] array, int index) { + array[index] = (byte) (value >> 24); + array[index+1] = (byte) (value >> 16); + array[index+2] = (byte) (value >> 8); + array[index+3] = (byte) (value); + } + + + static short shortFromBigEndian(byte[] array, int index) { + return (short) (((array[index] & 0xff) << 8) | + (array[index+1] & 0xff)); + } + + + static void shortToBigEndian(short value, byte[] array, int index) { + array[index] = (byte) (value >> 8); + array[index+1] = (byte) (value); + } + + + /* + * fileName may be an absolute or a relative file specification. + * Relative file names are looked for in several places: first, relative + * to any directories specified by the java.iccprofile.path property; + * second, relative to any directories specified by the java.class.path + * property; finally, in a directory used to store profiles always + * available, such as a profile for sRGB. Built-in profiles use .pf as + * the file name extension for profiles, e.g. sRGB.pf. + */ + private static FileInputStream openProfile(final String fileName) { + return (FileInputStream)java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Object run() { + return privilegedOpenProfile(fileName); + } + }); + } + + /* + * this version is called from doPrivileged in privilegedOpenProfile. + * the whole method is privileged! + */ + private static FileInputStream privilegedOpenProfile(String fileName) { + FileInputStream fis = null; + String path, dir, fullPath; + + File f = new File(fileName); /* try absolute file name */ + + if ((!f.isFile()) && + ((path = System.getProperty("java.iccprofile.path")) != null)){ + /* try relative to java.iccprofile.path */ + StringTokenizer st = + new StringTokenizer(path, File.pathSeparator); + while (st.hasMoreTokens() && (!f.isFile())) { + dir = st.nextToken(); + fullPath = dir + File.separatorChar + fileName; + f = new File(fullPath); + } + } + + if ((!f.isFile()) && + ((path = System.getProperty("java.class.path")) != null)) { + /* try relative to java.class.path */ + StringTokenizer st = + new StringTokenizer(path, File.pathSeparator); + while (st.hasMoreTokens() && (!f.isFile())) { + dir = st.nextToken(); + fullPath = dir + File.separatorChar + fileName; + f = new File(fullPath); + } + } + + if (!f.isFile()) { /* try the directory of built-in profiles */ + dir = System.getProperty("java.home") + + File.separatorChar + "lib" + File.separatorChar + "cmm"; + fullPath = dir + File.separatorChar + fileName; + f = new File(fullPath); + } + + if (f.isFile()) { + try { + fis = new FileInputStream(f); + } catch (FileNotFoundException e) { + } + } + return fis; + } + + + /* + * Serialization support. + * + * Directly deserialized profiles are useless since they are not + * registered with CMM. We don't allow constructor to be called + * directly and instead have clients to call one of getInstance + * factory methods that will register the profile with CMM. For + * deserialization we implement readResolve method that will + * resolve the bogus deserialized profile object with one obtained + * with getInstance as well. + * + * There're two primary factory methods for construction of ICC + * profiles: getInstance(int cspace) and getInstance(byte[] data). + * This implementation of ICC_Profile uses the former to return a + * cached singleton profile object, other implementations will + * likely use this technique too. To preserve the singleton + * pattern across serialization we serialize cached singleton + * profiles in such a way that deserializing VM could call + * getInstance(int cspace) method that will resolve deserialized + * object into the corresponding singleton as well. + * + * Since the singletons are private to ICC_Profile the readResolve + * method have to be `protected' instead of `private' so that + * singletons that are instances of subclasses of ICC_Profile + * could be correctly deserialized. + */ + + + /** + * Version of the format of additional serialized data in the + * stream. Version 1 corresponds to Java 2 + * Platform, v1.3. + * @since 1.3 + * @serial + */ + private int iccProfileSerializedDataVersion = 1; + + + /** + * Writes default serializable fields to the stream. Writes a + * string and an array of bytes to the stream as additional data. + * + * @param s stream used for serialization. + * @throws IOException + * thrown by ObjectInputStream. + * @serialData + * The String is the name of one of + * CS_* constants defined in the + * {@link ColorSpace} class if the profile object is a profile + * for a predefined color space (for example + * "CS_sRGB"). The string is null + * otherwise. + *

+ * The byte[] array is the profile data for the + * profile. For predefined color spaces null is + * written instead of the profile data. If in the future + * versions of Java API new predefined color spaces will be + * added, future versions of this class may choose to write + * for new predefined color spaces not only the color space + * name, but the profile data as well so that older versions + * could still deserialize the object. + */ + private void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + + String csName = null; + if (this == sRGBprofile) { + csName = "CS_sRGB"; + } else if (this == XYZprofile) { + csName = "CS_CIEXYZ"; + } else if (this == PYCCprofile) { + csName = "CS_PYCC"; + } else if (this == GRAYprofile) { + csName = "CS_GRAY"; + } else if (this == LINEAR_RGBprofile) { + csName = "CS_LINEAR_RGB"; + } + + // Future versions may choose to write profile data for new + // predefined color spaces as well, if any will be introduced, + // so that old versions that don't recognize the new CS name + // may fall back to constructing profile from the data. + byte[] data = null; + if (csName == null) { + // getData will activate deferred profile if necessary + data = getData(); + } + + s.writeObject(csName); + s.writeObject(data); + } + + // Temporary storage used by readObject to store resolved profile + // (obtained with getInstance) for readResolve to return. + private transient ICC_Profile resolvedDeserializedProfile; + + /** + * Reads default serializable fields from the stream. Reads from + * the stream a string and an array of bytes as additional data. + * + * @param s stream used for deserialization. + * @throws IOException + * thrown by ObjectInputStream. + * @throws ClassNotFoundException + * thrown by ObjectInputStream. + * @serialData + * The String is the name of one of + * CS_* constants defined in the + * {@link ColorSpace} class if the profile object is a profile + * for a predefined color space (for example + * "CS_sRGB"). The string is null + * otherwise. + *

+ * The byte[] array is the profile data for the + * profile. It will usually be null for the + * predefined profiles. + *

+ * If the string is recognized as a constant name for + * predefined color space the object will be resolved into + * profile obtained with + * getInstance(int cspace) and the profile + * data are ignored. Otherwise the object will be resolved + * into profile obtained with + * getInstance(byte[] data). + * @see #readResolve() + * @see #getInstance(int) + * @see #getInstance(byte[]) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + + String csName = (String)s.readObject(); + byte[] data = (byte[])s.readObject(); + + int cspace = 0; // ColorSpace.CS_* constant if known + boolean isKnownPredefinedCS = false; + if (csName != null) { + isKnownPredefinedCS = true; + if (csName.equals("CS_sRGB")) { + cspace = ColorSpace.CS_sRGB; + } else if (csName.equals("CS_CIEXYZ")) { + cspace = ColorSpace.CS_CIEXYZ; + } else if (csName.equals("CS_PYCC")) { + cspace = ColorSpace.CS_PYCC; + } else if (csName.equals("CS_GRAY")) { + cspace = ColorSpace.CS_GRAY; + } else if (csName.equals("CS_LINEAR_RGB")) { + cspace = ColorSpace.CS_LINEAR_RGB; + } else { + isKnownPredefinedCS = false; + } + } + + if (isKnownPredefinedCS) { + resolvedDeserializedProfile = getInstance(cspace); + } else { + resolvedDeserializedProfile = getInstance(data); + } + } + + /** + * Resolves instances being deserialized into instances registered + * with CMM. + * @return ICC_Profile object for profile registered with CMM. + * @throws ObjectStreamException + * never thrown, but mandated by the serialization spec. + * @since 1.3 + */ + protected Object readResolve() throws ObjectStreamException { + return resolvedDeserializedProfile; + } +} diff --git a/jdk/src/share/classes/java/awt/color/ICC_ProfileGray.java b/jdk/src/share/classes/java/awt/color/ICC_ProfileGray.java new file mode 100644 index 00000000000..0228515d64d --- /dev/null +++ b/jdk/src/share/classes/java/awt/color/ICC_ProfileGray.java @@ -0,0 +1,150 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/********************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.color; + +import java.awt.image.LookupTable; +import sun.java2d.cmm.ProfileDeferralInfo; + +/** + * + * A subclass of the ICC_Profile class which represents profiles + * which meet the following criteria: the color space type of the + * profile is TYPE_GRAY and the profile includes the grayTRCTag and + * mediaWhitePointTag tags. Examples of this kind of profile are + * monochrome input profiles, monochrome display profiles, and + * monochrome output profiles. The getInstance methods in the + * ICC_Profile class will + * return an ICC_ProfileGray object when the above conditions are + * met. The advantage of this class is that it provides a lookup + * table that Java or native methods may be able to use directly to + * optimize color conversion in some cases. + *

+ * To transform from a GRAY device profile color space to the CIEXYZ Profile + * Connection Space, the device gray component is transformed by + * a lookup through the tone reproduction curve (TRC). The result is + * treated as the achromatic component of the PCS. +

+
+                PCSY = grayTRC[deviceGray]
+
+
+ * The inverse transform is done by converting the PCS Y components to + * device Gray via the inverse of the grayTRC. + *

+ */ + + + +public class ICC_ProfileGray +extends ICC_Profile { + + static final long serialVersionUID = -1124721290732002649L; + + /** + * Constructs a new ICC_ProfileGray from a CMM ID. + */ + ICC_ProfileGray(long ID) { + super(ID); + } + + /** + * Constructs a new ICC_ProfileGray from a ProfileDeferralInfo object. + */ + ICC_ProfileGray(ProfileDeferralInfo pdi) { + super(pdi); + } + + + /** + * Returns a float array of length 3 containing the X, Y, and Z + * components of the mediaWhitePointTag in the ICC profile. + * @return an array containing the components of the + * mediaWhitePointTag in the ICC profile. + */ + public float[] getMediaWhitePoint() { + return super.getMediaWhitePoint(); + } + + + /** + * Returns a gamma value representing the tone reproduction + * curve (TRC). If the profile represents the TRC as a table rather + * than a single gamma value, then an exception is thrown. In this + * case the actual table can be obtained via getTRC(). When + * using a gamma value, the PCS Y component is computed as follows: +

+
+                          gamma
+         PCSY = deviceGray
+
+
+ * @return the gamma value as a float. + * @exception ProfileDataException if the profile does not specify + * the TRC as a single gamma value. + */ + public float getGamma() { + float theGamma; + + theGamma = super.getGamma(ICC_Profile.icSigGrayTRCTag); + return theGamma; + } + + /** + * Returns the TRC as an array of shorts. If the profile has + * specified the TRC as linear (gamma = 1.0) or as a simple gamma + * value, this method throws an exception, and the getGamma() method + * should be used to get the gamma value. Otherwise the short array + * returned here represents a lookup table where the input Gray value + * is conceptually in the range [0.0, 1.0]. Value 0.0 maps + * to array index 0 and value 1.0 maps to array index length-1. + * Interpolation may be used to generate output values for + * input values which do not map exactly to an index in the + * array. Output values also map linearly to the range [0.0, 1.0]. + * Value 0.0 is represented by an array value of 0x0000 and + * value 1.0 by 0xFFFF, i.e. the values are really unsigned + * short values, although they are returned in a short array. + * @return a short array representing the TRC. + * @exception ProfileDataException if the profile does not specify + * the TRC as a table. + */ + public short[] getTRC() { + short[] theTRC; + + theTRC = super.getTRC(ICC_Profile.icSigGrayTRCTag); + return theTRC; + } + +} diff --git a/jdk/src/share/classes/java/awt/color/ICC_ProfileRGB.java b/jdk/src/share/classes/java/awt/color/ICC_ProfileRGB.java new file mode 100644 index 00000000000..da9eb7999cc --- /dev/null +++ b/jdk/src/share/classes/java/awt/color/ICC_ProfileRGB.java @@ -0,0 +1,282 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/********************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.color; + +import java.awt.image.LookupTable; +import sun.java2d.cmm.ProfileDeferralInfo; + +/** + * + * The ICC_ProfileRGB class is a subclass of the ICC_Profile class + * that represents profiles which meet the following criteria: + *
    + *
  • The profile's color space type is RGB.
  • + *
  • The profile includes the redColorantTag, + * greenColorantTag, blueColorantTag, + * redTRCTag, greenTRCTag, + * blueTRCTag, and mediaWhitePointTag tags.
  • + *
+ * The ICC_Profile getInstance method will + * return an ICC_ProfileRGB object when these conditions are met. + * Three-component, matrix-based input profiles and RGB display profiles are + * examples of this type of profile. + *

+ * This profile class provides color transform matrices and lookup tables + * that Java or native methods can use directly to + * optimize color conversion in some cases. + *

+ * To transform from a device profile color space to the CIEXYZ Profile + * Connection Space, each device color component is first linearized by + * a lookup through the corresponding tone reproduction curve (TRC). + * The resulting linear RGB components are converted to the CIEXYZ PCS + * using a a 3x3 matrix constructed from the RGB colorants. + *

+ *
+ *                 linearR = redTRC[deviceR]
+ *
+ *                 linearG = greenTRC[deviceG]
+ *
+ *                 linearB = blueTRC[deviceB]
+ *
+ *   _      _       _                                             _   _         _
+ *  [  PCSX  ]     [  redColorantX  greenColorantX  blueColorantX  ] [  linearR  ]
+ *  [        ]     [                                               ] [           ]
+ *  [  PCSY  ]  =  [  redColorantY  greenColorantY  blueColorantY  ] [  linearG  ]
+ *  [        ]     [                                               ] [           ]
+ *  [_ PCSZ _]     [_ redColorantZ  greenColorantZ  blueColorantZ _] [_ linearB _]
+ *
+ * 
+ * The inverse transform is performed by converting PCS XYZ components to linear + * RGB components through the inverse of the above 3x3 matrix, and then converting + * linear RGB to device RGB through inverses of the TRCs. + *

+ */ + + + +public class ICC_ProfileRGB +extends ICC_Profile { + + static final long serialVersionUID = 8505067385152579334L; + + /** + * Used to get a gamma value or TRC for the red component. + */ + public static final int REDCOMPONENT = 0; + + /** + * Used to get a gamma value or TRC for the green component. + */ + public static final int GREENCOMPONENT = 1; + + /** + * Used to get a gamma value or TRC for the blue component. + */ + public static final int BLUECOMPONENT = 2; + + + /** + * Constructs an new ICC_ProfileRGB from a CMM ID. + * + * @param ID The CMM ID for the profile. + * + */ + ICC_ProfileRGB(long ID) { + super(ID); + } + + /** + * Constructs a new ICC_ProfileRGB from a + * ProfileDeferralInfo object. + * + * @param pdi + */ + ICC_ProfileRGB(ProfileDeferralInfo pdi) { + super(pdi); + } + + + /** + * Returns an array that contains the components of the profile's + * mediaWhitePointTag. + * + * @return A 3-element float array containing the x, y, + * and z components of the profile's mediaWhitePointTag. + */ + public float[] getMediaWhitePoint() { + return super.getMediaWhitePoint(); + } + + + /** + * Returns a 3x3 float matrix constructed from the + * X, Y, and Z components of the profile's redColorantTag, + * greenColorantTag, and blueColorantTag. + *

+ * This matrix can be used for color transforms in the forward + * direction of the profile--from the profile color space + * to the CIEXYZ PCS. + * + * @return A 3x3 float array that contains the x, y, and z + * components of the profile's redColorantTag, + * greenColorantTag, and blueColorantTag. + */ + public float[][] getMatrix() { + float[][] theMatrix = new float[3][3]; + float[] tmpMatrix; + + tmpMatrix = getXYZTag(ICC_Profile.icSigRedColorantTag); + theMatrix[0][0] = tmpMatrix[0]; + theMatrix[1][0] = tmpMatrix[1]; + theMatrix[2][0] = tmpMatrix[2]; + tmpMatrix = getXYZTag(ICC_Profile.icSigGreenColorantTag); + theMatrix[0][1] = tmpMatrix[0]; + theMatrix[1][1] = tmpMatrix[1]; + theMatrix[2][1] = tmpMatrix[2]; + tmpMatrix = getXYZTag(ICC_Profile.icSigBlueColorantTag); + theMatrix[0][2] = tmpMatrix[0]; + theMatrix[1][2] = tmpMatrix[1]; + theMatrix[2][2] = tmpMatrix[2]; + return theMatrix; + } + + /** + * Returns a gamma value representing the tone reproduction curve + * (TRC) for a particular component. The component parameter + * must be one of REDCOMPONENT, GREENCOMPONENT, or BLUECOMPONENT. + *

+ * If the profile + * represents the TRC for the corresponding component + * as a table rather than a single gamma value, an + * exception is thrown. In this case the actual table + * can be obtained through the {@link #getTRC(int)} method. + * When using a gamma value, + * the linear component (R, G, or B) is computed as follows: + *

+     *
+     *                                           gamma
+     *          linearComponent = deviceComponent
+     *
+     *
+ * @param component The ICC_ProfileRGB constant that + * represents the component whose TRC you want to retrieve + * @return the gamma value as a float. + * @exception ProfileDataException if the profile does not specify + * the corresponding TRC as a single gamma value. + */ + public float getGamma(int component) { + float theGamma; + int theSignature; + + switch (component) { + case REDCOMPONENT: + theSignature = ICC_Profile.icSigRedTRCTag; + break; + + case GREENCOMPONENT: + theSignature = ICC_Profile.icSigGreenTRCTag; + break; + + case BLUECOMPONENT: + theSignature = ICC_Profile.icSigBlueTRCTag; + break; + + default: + throw new IllegalArgumentException("Must be Red, Green, or Blue"); + } + + theGamma = super.getGamma(theSignature); + + return theGamma; + } + + /** + * Returns the TRC for a particular component as an array. + * Component must be REDCOMPONENT, + * GREENCOMPONENT, or BLUECOMPONENT. + * Otherwise the returned array + * represents a lookup table where the input component value + * is conceptually in the range [0.0, 1.0]. Value 0.0 maps + * to array index 0 and value 1.0 maps to array index length-1. + * Interpolation might be used to generate output values for + * input values that do not map exactly to an index in the + * array. Output values also map linearly to the range [0.0, 1.0]. + * Value 0.0 is represented by an array value of 0x0000 and + * value 1.0 by 0xFFFF. In other words, the values are really unsigned + * short values even though they are returned in a + * short array. + * + * If the profile has specified the corresponding TRC + * as linear (gamma = 1.0) or as a simple gamma value, this method + * throws an exception. In this case, the {@link #getGamma(int)} + * method should be used to get the gamma value. + * + * @param component The ICC_ProfileRGB constant that + * represents the component whose TRC you want to retrieve: + * REDCOMPONENT, GREENCOMPONENT, or + * BLUECOMPONENT. + * + * @return a short array representing the TRC. + * @exception ProfileDataException if the profile does not specify + * the corresponding TRC as a table. + */ + public short[] getTRC(int component) { + short[] theTRC; + int theSignature; + + switch (component) { + case REDCOMPONENT: + theSignature = ICC_Profile.icSigRedTRCTag; + break; + + case GREENCOMPONENT: + theSignature = ICC_Profile.icSigGreenTRCTag; + break; + + case BLUECOMPONENT: + theSignature = ICC_Profile.icSigBlueTRCTag; + break; + + default: + throw new IllegalArgumentException("Must be Red, Green, or Blue"); + } + + theTRC = super.getTRC(theSignature); + + return theTRC; + } + +} diff --git a/jdk/src/share/classes/java/awt/image/BandedSampleModel.java b/jdk/src/share/classes/java/awt/image/BandedSampleModel.java new file mode 100644 index 00000000000..ce7aa5382b5 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/BandedSampleModel.java @@ -0,0 +1,839 @@ +/* + * Portions Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +/** + * This class represents image data which is stored in a band interleaved + * fashion and for + * which each sample of a pixel occupies one data element of the DataBuffer. + * It subclasses ComponentSampleModel but provides a more efficent + * implementation for accessing band interleaved image data than is provided + * by ComponentSampleModel. This class should typically be used when working + * with images which store sample data for each band in a different bank of the + * DataBuffer. Accessor methods are provided so that image data can be + * manipulated directly. Pixel stride is the number of + * data array elements between two samples for the same band on the same + * scanline. The pixel stride for a BandedSampleModel is one. + * Scanline stride is the number of data array elements between + * a given sample and the corresponding sample in the same column of the next + * scanline. Band offsets denote the number + * of data array elements from the first data array element of the bank + * of the DataBuffer holding each band to the first sample of the band. + * The bands are numbered from 0 to N-1. + * Bank indices denote the correspondence between a bank of the data buffer + * and a band of image data. This class supports + * {@link DataBuffer#TYPE_BYTE TYPE_BYTE}, + * {@link DataBuffer#TYPE_USHORT TYPE_USHORT}, + * {@link DataBuffer#TYPE_SHORT TYPE_SHORT}, + * {@link DataBuffer#TYPE_INT TYPE_INT}, + * {@link DataBuffer#TYPE_FLOAT TYPE_FLOAT}, and + * {@link DataBuffer#TYPE_DOUBLE TYPE_DOUBLE} datatypes + */ + + +public final class BandedSampleModel extends ComponentSampleModel +{ + + /** + * Constructs a BandedSampleModel with the specified parameters. + * The pixel stride will be one data element. The scanline stride + * will be the same as the width. Each band will be stored in + * a separate bank and all band offsets will be zero. + * @param dataType The data type for storing samples. + * @param w The width (in pixels) of the region of + * image data described. + * @param h The height (in pixels) of the region of image + * data described. + * @param numBands The number of bands for the image data. + * @throws IllegalArgumentException if dataType is not + * one of the supported data types + */ + public BandedSampleModel(int dataType, int w, int h, int numBands) { + super(dataType, w, h, 1, w, + BandedSampleModel.createIndicesArray(numBands), + BandedSampleModel.createOffsetArray(numBands)); + } + + /** + * Constructs a BandedSampleModel with the specified parameters. + * The number of bands will be inferred from the lengths of the + * bandOffsets bankIndices arrays, which must be equal. The pixel + * stride will be one data element. + * @param dataType The data type for storing samples. + * @param w The width (in pixels) of the region of + * image data described. + * @param h The height (in pixels) of the region of + * image data described. + * @param scanlineStride The line stride of the of the image data. + * @param bankIndices The bank index for each band. + * @param bandOffsets The band offset for each band. + * @throws IllegalArgumentException if dataType is not + * one of the supported data types + */ + public BandedSampleModel(int dataType, + int w, int h, + int scanlineStride, + int bankIndices[], + int bandOffsets[]) { + + super(dataType, w, h, 1,scanlineStride, bankIndices, bandOffsets); + } + + /** + * Creates a new BandedSampleModel with the specified + * width and height. The new BandedSampleModel will have the same + * number of bands, storage data type, and bank indices + * as this BandedSampleModel. The band offsets will be compressed + * such that the offset between bands will be w*pixelStride and + * the minimum of all of the band offsets is zero. + * @param w the width of the resulting BandedSampleModel + * @param h the height of the resulting BandedSampleModel + * @return a new BandedSampleModel with the specified + * width and height. + * @throws IllegalArgumentException if w or + * h equals either + * Integer.MAX_VALUE or + * Integer.MIN_VALUE + * @throws IllegalArgumentException if dataType is not + * one of the supported data types + */ + public SampleModel createCompatibleSampleModel(int w, int h) { + int[] bandOffs; + + if (numBanks == 1) { + bandOffs = orderBands(bandOffsets, w*h); + } + else { + bandOffs = new int[bandOffsets.length]; + } + + SampleModel sampleModel = + new BandedSampleModel(dataType, w, h, w, bankIndices, bandOffs); + return sampleModel; + } + + /** + * Creates a new BandedSampleModel with a subset of the bands of this + * BandedSampleModel. The new BandedSampleModel can be + * used with any DataBuffer that the existing BandedSampleModel + * can be used with. The new BandedSampleModel/DataBuffer + * combination will represent an image with a subset of the bands + * of the original BandedSampleModel/DataBuffer combination. + * @throws RasterFormatException if the number of bands is greater than + * the number of banks in this sample model. + * @throws IllegalArgumentException if dataType is not + * one of the supported data types + */ + public SampleModel createSubsetSampleModel(int bands[]) { + if (bands.length > bankIndices.length) + throw new RasterFormatException("There are only " + + bankIndices.length + + " bands"); + int newBankIndices[] = new int[bands.length]; + int newBandOffsets[] = new int[bands.length]; + + for (int i=0; idataType is not + * one of the supported types. + */ + public DataBuffer createDataBuffer() { + DataBuffer dataBuffer = null; + + int size = scanlineStride * height; + switch (dataType) { + case DataBuffer.TYPE_BYTE: + dataBuffer = new DataBufferByte(size, numBanks); + break; + case DataBuffer.TYPE_USHORT: + dataBuffer = new DataBufferUShort(size, numBanks); + break; + case DataBuffer.TYPE_SHORT: + dataBuffer = new DataBufferShort(size, numBanks); + break; + case DataBuffer.TYPE_INT: + dataBuffer = new DataBufferInt(size, numBanks); + break; + case DataBuffer.TYPE_FLOAT: + dataBuffer = new DataBufferFloat(size, numBanks); + break; + case DataBuffer.TYPE_DOUBLE: + dataBuffer = new DataBufferDouble(size, numBanks); + break; + default: + throw new IllegalArgumentException("dataType is not one " + + "of the supported types."); + } + + return dataBuffer; + } + + + /** + * Returns data for a single pixel in a primitive array of type + * TransferType. For a BandedSampleModel, this will be the same + * as the data type, and samples will be returned one per array + * element. Generally, obj + * should be passed in as null, so that the Object will be created + * automatically and will be of the right primitive data type. + *

+ * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is described by + * BandedSampleModel bsm1, to DataBuffer db2, + * whose storage layout is described by + * BandedSampleModel bsm2. + * The transfer will generally be more efficient than using + * getPixel/setPixel. + *

+     *       BandedSampleModel bsm1, bsm2;
+     *       DataBufferInt db1, db2;
+     *       bsm2.setDataElements(x, y, bsm1.getDataElements(x, y, null, db1),
+     *                            db2);
+     * 
+ * Using getDataElements/setDataElements to transfer between two + * DataBuffer/SampleModel pairs is legitimate if the SampleModels have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * If obj is non-null, it should be a primitive array of type TransferType. + * Otherwise, a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if obj is non-null and is not large enough to hold + * the pixel data. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param obj If non-null, a primitive array in which to return + * the pixel data. + * @param data The DataBuffer containing the image data. + * @return the data for the specified pixel. + * @see #setDataElements(int, int, Object, DataBuffer) + */ + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int type = getTransferType(); + int numDataElems = getNumDataElements(); + int pixelOffset = y*scanlineStride + x; + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] bdata; + + if (obj == null) { + bdata = new byte[numDataElems]; + } else { + bdata = (byte[])obj; + } + + for (int i=0; i= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int[] pixels; + + if (iArray != null) { + pixels = iArray; + } else { + pixels = new int [numBands]; + } + + int pixelOffset = y*scanlineStride + x; + for (int i=0; i width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int[] pixels; + + if (iArray != null) { + pixels = iArray; + } else { + pixels = new int[w*h*numBands]; + } + + for (int k = 0; k < numBands; k++) { + int lineOffset = y*scanlineStride + x + bandOffsets[k]; + int srcOffset = k; + int bank = bankIndices[k]; + + for (int i = 0; i < h; i++) { + int pixelOffset = lineOffset; + for (int j = 0; j < w; j++) { + pixels[srcOffset] = data.getElem(bank, pixelOffset++); + srcOffset += numBands; + } + lineOffset += scanlineStride; + } + } + return pixels; + } + + /** + * Returns as int the sample in a specified band for the pixel + * located at (x,y). + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to return + * @param data The DataBuffer containing the image data + * @return the sample in the specified band for the specified pixel. + * @see #setSample(int, int, int, int, DataBuffer) + */ + public int getSample(int x, int y, int b, DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int sample = + data.getElem(bankIndices[b], + y*scanlineStride + x + bandOffsets[b]); + return sample; + } + + /** + * Returns the sample in a specified band + * for the pixel located at (x,y) as a float. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to return + * @param data The DataBuffer containing the image data + * @return a float value that represents the sample in the specified + * band for the specified pixel. + */ + public float getSampleFloat(int x, int y, int b, DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + float sample = data.getElemFloat(bankIndices[b], + y*scanlineStride + x + bandOffsets[b]); + return sample; + } + + /** + * Returns the sample in a specified band + * for a pixel located at (x,y) as a double. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to return + * @param data The DataBuffer containing the image data + * @return a double value that represents the sample in the specified + * band for the specified pixel. + */ + public double getSampleDouble(int x, int y, int b, DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + double sample = data.getElemDouble(bankIndices[b], + y*scanlineStride + x + bandOffsets[b]); + return sample; + } + + /** + * Returns the samples in a specified band for the specified rectangle + * of pixels in an int array, one sample per data array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the upper left pixel location + * @param y The Y coordinate of the upper left pixel location + * @param w The width of the pixel rectangle + * @param h The height of the pixel rectangle + * @param b The band to return + * @param iArray If non-null, returns the samples in this array + * @param data The DataBuffer containing the image data + * @return the samples in the specified band for the pixels within + * the specified region. + * @see #setSamples(int, int, int, int, int, int[], DataBuffer) + */ + public int[] getSamples(int x, int y, int w, int h, int b, + int iArray[], DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int samples[]; + if (iArray != null) { + samples = iArray; + } else { + samples = new int [w*h]; + } + + int lineOffset = y*scanlineStride + x + bandOffsets[b]; + int srcOffset = 0; + int bank = bankIndices[b]; + + for (int i = 0; i < h; i++) { + int sampleOffset = lineOffset; + for (int j = 0; j < w; j++) { + samples[srcOffset++] = data.getElem(bank, sampleOffset++); + } + lineOffset += scanlineStride; + } + return samples; + } + + /** + * Sets the data for a single pixel in the specified DataBuffer from a + * primitive array of type TransferType. For a BandedSampleModel, + * this will be the same as the data type, and samples are transferred + * one per array element. + *

+ * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is described by + * BandedSampleModel bsm1, to DataBuffer db2, + * whose storage layout is described by + * BandedSampleModel bsm2. + * The transfer will generally be more efficient than using + * getPixel/setPixel. + *

+     *       BandedSampleModel bsm1, bsm2;
+     *       DataBufferInt db1, db2;
+     *       bsm2.setDataElements(x, y, bsm1.getDataElements(x, y, null, db1),
+     *                            db2);
+     * 
+ * Using getDataElements/setDataElements to transfer between two + * DataBuffer/SampleModel pairs is legitimate if the SampleModels have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * obj must be a primitive array of type TransferType. Otherwise, + * a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if obj is not large enough to hold the pixel data. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param obj If non-null, returns the primitive array in this + * object + * @param data The DataBuffer containing the image data + * @see #getDataElements(int, int, Object, DataBuffer) + */ + public void setDataElements(int x, int y, Object obj, DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int type = getTransferType(); + int numDataElems = getNumDataElements(); + int pixelOffset = y*scanlineStride + x; + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] barray = (byte[])obj; + + for (int i=0; i= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int pixelOffset = y*scanlineStride + x; + for (int i=0; i width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + for (int k = 0; k < numBands; k++) { + int lineOffset = y*scanlineStride + x + bandOffsets[k]; + int srcOffset = k; + int bank = bankIndices[k]; + + for (int i = 0; i < h; i++) { + int pixelOffset = lineOffset; + for (int j = 0; j < w; j++) { + data.setElem(bank, pixelOffset++, iArray[srcOffset]); + srcOffset += numBands; + } + lineOffset += scanlineStride; + } + } + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using an int for input. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to set + * @param s The input sample as an int + * @param data The DataBuffer containing the image data + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, int s, + DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + data.setElem(bankIndices[b], + y*scanlineStride + x + bandOffsets[b], s); + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using a float for input. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to set + * @param s The input sample as a float + * @param data The DataBuffer containing the image data + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, + float s , + DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + data.setElemFloat(bankIndices[b], + y*scanlineStride + x + bandOffsets[b], s); + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using a double for input. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to set + * @param s The input sample as a double + * @param data The DataBuffer containing the image data + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, + double s, + DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + data.setElemDouble(bankIndices[b], + y*scanlineStride + x + bandOffsets[b], s); + } + + /** + * Sets the samples in the specified band for the specified rectangle + * of pixels from an int array containing one sample per data array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the upper left pixel location + * @param y The Y coordinate of the upper left pixel location + * @param w The width of the pixel rectangle + * @param h The height of the pixel rectangle + * @param b The band to set + * @param iArray The input sample array + * @param data The DataBuffer containing the image data + * @see #getSamples(int, int, int, int, int, int[], DataBuffer) + */ + public void setSamples(int x, int y, int w, int h, int b, + int iArray[], DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int lineOffset = y*scanlineStride + x + bandOffsets[b]; + int srcOffset = 0; + int bank = bankIndices[b]; + + for (int i = 0; i < h; i++) { + int sampleOffset = lineOffset; + for (int j = 0; j < w; j++) { + data.setElem(bank, sampleOffset++, iArray[srcOffset++]); + } + lineOffset += scanlineStride; + } + } + + private static int[] createOffsetArray(int numBands) { + int[] bandOffsets = new int[numBands]; + for (int i=0; i < numBands; i++) { + bandOffsets[i] = 0; + } + return bandOffsets; + } + + private static int[] createIndicesArray(int numBands) { + int[] bankIndices = new int[numBands]; + for (int i=0; i < numBands; i++) { + bankIndices[i] = i; + } + return bankIndices; + } + + // Differentiate hash code from other ComponentSampleModel subclasses + public int hashCode() { + return super.hashCode() ^ 0x2; + } +} diff --git a/jdk/src/share/classes/java/awt/image/ColorConvertOp.java b/jdk/src/share/classes/java/awt/image/ColorConvertOp.java new file mode 100644 index 00000000000..2b67153b555 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/ColorConvertOp.java @@ -0,0 +1,1109 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/********************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.image; + +import java.awt.Point; +import java.awt.Graphics2D; +import java.awt.color.*; +import sun.java2d.cmm.ColorTransform; +import sun.java2d.cmm.CMSManager; +import sun.java2d.cmm.ProfileDeferralMgr; +import sun.java2d.cmm.PCMM; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Point2D; +import java.awt.RenderingHints; + +/** + * This class performs a pixel-by-pixel color conversion of the data in + * the source image. The resulting color values are scaled to the precision + * of the destination image. Color conversion can be specified + * via an array of ColorSpace objects or an array of ICC_Profile objects. + *

+ * If the source is a BufferedImage with premultiplied alpha, the + * color components are divided by the alpha component before color conversion. + * If the destination is a BufferedImage with premultiplied alpha, the + * color components are multiplied by the alpha component after conversion. + * Rasters are treated as having no alpha channel, i.e. all bands are + * color bands. + *

+ * If a RenderingHints object is specified in the constructor, the + * color rendering hint and the dithering hint may be used to control + * color conversion. + *

+ * Note that Source and Destination may be the same object. + *

+ * @see java.awt.RenderingHints#KEY_COLOR_RENDERING + * @see java.awt.RenderingHints#KEY_DITHERING + */ +public class ColorConvertOp implements BufferedImageOp, RasterOp { + ICC_Profile[] profileList; + ColorSpace[] CSList; + ColorTransform thisTransform, thisRasterTransform; + ICC_Profile thisSrcProfile, thisDestProfile; + RenderingHints hints; + boolean gotProfiles; + float[] srcMinVals, srcMaxVals, dstMinVals, dstMaxVals; + + /* the class initializer */ + static { + if (ProfileDeferralMgr.deferring) { + ProfileDeferralMgr.activateProfiles(); + } + } + + /** + * Constructs a new ColorConvertOp which will convert + * from a source color space to a destination color space. + * The RenderingHints argument may be null. + * This Op can be used only with BufferedImages, and will convert + * directly from the ColorSpace of the source image to that of the + * destination. The destination argument of the filter method + * cannot be specified as null. + * @param hints the RenderingHints object used to control + * the color conversion, or null + */ + public ColorConvertOp (RenderingHints hints) + { + profileList = new ICC_Profile [0]; /* 0 length list */ + this.hints = hints; + } + + /** + * Constructs a new ColorConvertOp from a ColorSpace object. + * The RenderingHints argument may be null. This + * Op can be used only with BufferedImages, and is primarily useful + * when the {@link #filter(BufferedImage, BufferedImage) filter} + * method is invoked with a destination argument of null. + * In that case, the ColorSpace defines the destination color space + * for the destination created by the filter method. Otherwise, the + * ColorSpace defines an intermediate space to which the source is + * converted before being converted to the destination space. + * @param cspace defines the destination ColorSpace or an + * intermediate ColorSpace + * @param hints the RenderingHints object used to control + * the color conversion, or null + * @throws NullPointerException if cspace is null + */ + public ColorConvertOp (ColorSpace cspace, RenderingHints hints) + { + if (cspace == null) { + throw new NullPointerException("ColorSpace cannot be null"); + } + if (cspace instanceof ICC_ColorSpace) { + profileList = new ICC_Profile [1]; /* 1 profile in the list */ + + profileList [0] = ((ICC_ColorSpace) cspace).getProfile(); + } + else { + CSList = new ColorSpace[1]; /* non-ICC case: 1 ColorSpace in list */ + CSList[0] = cspace; + } + this.hints = hints; + } + + + /** + * Constructs a new ColorConvertOp from two ColorSpace objects. + * The RenderingHints argument may be null. + * This Op is primarily useful for calling the filter method on + * Rasters, in which case the two ColorSpaces define the operation + * to be performed on the Rasters. In that case, the number of bands + * in the source Raster must match the number of components in + * srcCspace, and the number of bands in the destination Raster + * must match the number of components in dstCspace. For BufferedImages, + * the two ColorSpaces define intermediate spaces through which the + * source is converted before being converted to the destination space. + * @param srcCspace the source ColorSpace + * @param dstCspace the destination ColorSpace + * @param hints the RenderingHints object used to control + * the color conversion, or null + * @throws NullPointerException if either srcCspace or dstCspace is null + */ + public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace, + RenderingHints hints) + { + if ((srcCspace == null) || (dstCspace == null)) { + throw new NullPointerException("ColorSpaces cannot be null"); + } + if ((srcCspace instanceof ICC_ColorSpace) && + (dstCspace instanceof ICC_ColorSpace)) { + profileList = new ICC_Profile [2]; /* 2 profiles in the list */ + + profileList [0] = ((ICC_ColorSpace) srcCspace).getProfile(); + profileList [1] = ((ICC_ColorSpace) dstCspace).getProfile(); + + getMinMaxValsFromColorSpaces(srcCspace, dstCspace); + } else { + /* non-ICC case: 2 ColorSpaces in list */ + CSList = new ColorSpace[2]; + CSList[0] = srcCspace; + CSList[1] = dstCspace; + } + this.hints = hints; + } + + + /** + * Constructs a new ColorConvertOp from an array of ICC_Profiles. + * The RenderingHints argument may be null. + * The sequence of profiles may include profiles that represent color + * spaces, profiles that represent effects, etc. If the whole sequence + * does not represent a well-defined color conversion, an exception is + * thrown. + *

For BufferedImages, if the ColorSpace + * of the source BufferedImage does not match the requirements of the + * first profile in the array, + * the first conversion is to an appropriate ColorSpace. + * If the requirements of the last profile in the array are not met + * by the ColorSpace of the destination BufferedImage, + * the last conversion is to the destination's ColorSpace. + *

For Rasters, the number of bands in the source Raster must match + * the requirements of the first profile in the array, and the + * number of bands in the destination Raster must match the requirements + * of the last profile in the array. The array must have at least two + * elements or calling the filter method for Rasters will throw an + * IllegalArgumentException. + * @param profiles the array of ICC_Profile objects + * @param hints the RenderingHints object used to control + * the color conversion, or null + * @exception IllegalArgumentException when the profile sequence does not + * specify a well-defined color conversion + * @exception NullPointerException if profiles is null + */ + public ColorConvertOp (ICC_Profile[] profiles, RenderingHints hints) + { + if (profiles == null) { + throw new NullPointerException("Profiles cannot be null"); + } + gotProfiles = true; + profileList = new ICC_Profile[profiles.length]; + for (int i1 = 0; i1 < profiles.length; i1++) { + profileList[i1] = profiles[i1]; + } + this.hints = hints; + } + + + /** + * Returns the array of ICC_Profiles used to construct this ColorConvertOp. + * Returns null if the ColorConvertOp was not constructed from such an + * array. + * @return the array of ICC_Profile objects of this + * ColorConvertOp, or null if this + * ColorConvertOp was not constructed with an + * array of ICC_Profile objects. + */ + public final ICC_Profile[] getICC_Profiles() { + if (gotProfiles) { + ICC_Profile[] profiles = new ICC_Profile[profileList.length]; + for (int i1 = 0; i1 < profileList.length; i1++) { + profiles[i1] = profileList[i1]; + } + return profiles; + } + return null; + } + + /** + * ColorConverts the source BufferedImage. + * If the destination image is null, + * a BufferedImage will be created with an appropriate ColorModel. + * @param src the source BufferedImage to be converted + * @param dest the destination BufferedImage, + * or null + * @return dest color converted from src + * or a new, converted BufferedImage + * if dest is null + * @exception IllegalArgumentException if dest is null and this op was + * constructed using the constructor which takes only a + * RenderingHints argument, since the operation is ill defined. + */ + public final BufferedImage filter(BufferedImage src, BufferedImage dest) { + ColorSpace srcColorSpace, destColorSpace; + BufferedImage savdest = null; + + if (src.getColorModel() instanceof IndexColorModel) { + IndexColorModel icm = (IndexColorModel) src.getColorModel(); + src = icm.convertToIntDiscrete(src.getRaster(), true); + } + srcColorSpace = src.getColorModel().getColorSpace(); + if (dest != null) { + if (dest.getColorModel() instanceof IndexColorModel) { + savdest = dest; + dest = null; + destColorSpace = null; + } else { + destColorSpace = dest.getColorModel().getColorSpace(); + } + } else { + destColorSpace = null; + } + + if ((CSList != null) || + (!(srcColorSpace instanceof ICC_ColorSpace)) || + ((dest != null) && + (!(destColorSpace instanceof ICC_ColorSpace)))) { + /* non-ICC case */ + dest = nonICCBIFilter(src, srcColorSpace, dest, destColorSpace); + } else { + dest = ICCBIFilter(src, srcColorSpace, dest, destColorSpace); + } + + if (savdest != null) { + Graphics2D big = savdest.createGraphics(); + try { + big.drawImage(dest, 0, 0, null); + } finally { + big.dispose(); + } + return savdest; + } else { + return dest; + } + } + + private final BufferedImage ICCBIFilter(BufferedImage src, + ColorSpace srcColorSpace, + BufferedImage dest, + ColorSpace destColorSpace) { + int nProfiles = profileList.length; + ICC_Profile srcProfile = null, destProfile = null; + + srcProfile = ((ICC_ColorSpace) srcColorSpace).getProfile(); + + if (dest == null) { /* last profile in the list defines + the output color space */ + if (nProfiles == 0) { + throw new IllegalArgumentException( + "Destination ColorSpace is undefined"); + } + destProfile = profileList [nProfiles - 1]; + dest = createCompatibleDestImage(src, null); + } + else { + if (src.getHeight() != dest.getHeight() || + src.getWidth() != dest.getWidth()) { + throw new IllegalArgumentException( + "Width or height of BufferedImages do not match"); + } + destProfile = ((ICC_ColorSpace) destColorSpace).getProfile(); + } + + /* Checking if all profiles in the transform sequence are the same. + * If so, performing just copying the data. + */ + if (srcProfile == destProfile) { + boolean noTrans = true; + for (int i = 0; i < nProfiles; i++) { + if (srcProfile != profileList[i]) { + noTrans = false; + break; + } + } + if (noTrans) { + Graphics2D g = dest.createGraphics(); + try { + g.drawImage(src, 0, 0, null); + } finally { + g.dispose(); + } + + return dest; + } + } + + /* make a new transform if needed */ + if ((thisTransform == null) || (thisSrcProfile != srcProfile) || + (thisDestProfile != destProfile) ) { + updateBITransform(srcProfile, destProfile); + } + + /* color convert the image */ + thisTransform.colorConvert(src, dest); + + return dest; + } + + private void updateBITransform(ICC_Profile srcProfile, + ICC_Profile destProfile) { + ICC_Profile[] theProfiles; + int i1, nProfiles, nTransforms, whichTrans, renderState; + ColorTransform[] theTransforms; + boolean useSrc = false, useDest = false; + + nProfiles = profileList.length; + nTransforms = nProfiles; + if ((nProfiles == 0) || (srcProfile != profileList[0])) { + nTransforms += 1; + useSrc = true; + } + if ((nProfiles == 0) || (destProfile != profileList[nProfiles - 1]) || + (nTransforms < 2)) { + nTransforms += 1; + useDest = true; + } + + /* make the profile list */ + theProfiles = new ICC_Profile[nTransforms]; /* the list of profiles + for this Op */ + + int idx = 0; + if (useSrc) { + /* insert source as first profile */ + theProfiles[idx++] = srcProfile; + } + + for (i1 = 0; i1 < nProfiles; i1++) { + /* insert profiles defined in this Op */ + theProfiles[idx++] = profileList [i1]; + } + + if (useDest) { + /* insert dest as last profile */ + theProfiles[idx] = destProfile; + } + + /* make the transform list */ + theTransforms = new ColorTransform [nTransforms]; + + /* initialize transform get loop */ + if (theProfiles[0].getProfileClass() == ICC_Profile.CLASS_OUTPUT) { + /* if first profile is a printer + render as colorimetric */ + renderState = ICC_Profile.icRelativeColorimetric; + } + else { + renderState = ICC_Profile.icPerceptual; /* render any other + class perceptually */ + } + + whichTrans = ColorTransform.In; + + PCMM mdl = CMSManager.getModule(); + + /* get the transforms from each profile */ + for (i1 = 0; i1 < nTransforms; i1++) { + if (i1 == nTransforms -1) { /* last profile? */ + whichTrans = ColorTransform.Out; /* get output transform */ + } + else { /* check for abstract profile */ + if ((whichTrans == ColorTransform.Simulation) && + (theProfiles[i1].getProfileClass () == + ICC_Profile.CLASS_ABSTRACT)) { + renderState = ICC_Profile.icPerceptual; + whichTrans = ColorTransform.In; + } + } + + theTransforms[i1] = mdl.createTransform ( + theProfiles[i1], renderState, whichTrans); + + /* get this profile's rendering intent to select transform + from next profile */ + renderState = getRenderingIntent(theProfiles[i1]); + + /* "middle" profiles use simulation transform */ + whichTrans = ColorTransform.Simulation; + } + + /* make the net transform */ + thisTransform = mdl.createTransform(theTransforms); + + /* update corresponding source and dest profiles */ + thisSrcProfile = srcProfile; + thisDestProfile = destProfile; + } + + /** + * ColorConverts the image data in the source Raster. + * If the destination Raster is null, a new Raster will be created. + * The number of bands in the source and destination Rasters must + * meet the requirements explained above. The constructor used to + * create this ColorConvertOp must have provided enough information + * to define both source and destination color spaces. See above. + * Otherwise, an exception is thrown. + * @param src the source Raster to be converted + * @param dest the destination WritableRaster, + * or null + * @return dest color converted from src + * or a new, converted WritableRaster + * if dest is null + * @exception IllegalArgumentException if the number of source or + * destination bands is incorrect, the source or destination + * color spaces are undefined, or this op was constructed + * with one of the constructors that applies only to + * operations on BufferedImages. + */ + public final WritableRaster filter (Raster src, WritableRaster dest) { + + if (CSList != null) { + /* non-ICC case */ + return nonICCRasterFilter(src, dest); + } + int nProfiles = profileList.length; + if (nProfiles < 2) { + throw new IllegalArgumentException( + "Source or Destination ColorSpace is undefined"); + } + if (src.getNumBands() != profileList[0].getNumComponents()) { + throw new IllegalArgumentException( + "Numbers of source Raster bands and source color space " + + "components do not match"); + } + if (dest == null) { + dest = createCompatibleDestRaster(src); + } + else { + if (src.getHeight() != dest.getHeight() || + src.getWidth() != dest.getWidth()) { + throw new IllegalArgumentException( + "Width or height of Rasters do not match"); + } + if (dest.getNumBands() != + profileList[nProfiles-1].getNumComponents()) { + throw new IllegalArgumentException( + "Numbers of destination Raster bands and destination " + + "color space components do not match"); + } + } + + /* make a new transform if needed */ + if (thisRasterTransform == null) { + int i1, whichTrans, renderState; + ColorTransform[] theTransforms; + + /* make the transform list */ + theTransforms = new ColorTransform [nProfiles]; + + /* initialize transform get loop */ + if (profileList[0].getProfileClass() == ICC_Profile.CLASS_OUTPUT) { + /* if first profile is a printer + render as colorimetric */ + renderState = ICC_Profile.icRelativeColorimetric; + } + else { + renderState = ICC_Profile.icPerceptual; /* render any other + class perceptually */ + } + + whichTrans = ColorTransform.In; + + PCMM mdl = CMSManager.getModule(); + + /* get the transforms from each profile */ + for (i1 = 0; i1 < nProfiles; i1++) { + if (i1 == nProfiles -1) { /* last profile? */ + whichTrans = ColorTransform.Out; /* get output transform */ + } + else { /* check for abstract profile */ + if ((whichTrans == ColorTransform.Simulation) && + (profileList[i1].getProfileClass () == + ICC_Profile.CLASS_ABSTRACT)) { + renderState = ICC_Profile.icPerceptual; + whichTrans = ColorTransform.In; + } + } + + theTransforms[i1] = mdl.createTransform ( + profileList[i1], renderState, whichTrans); + + /* get this profile's rendering intent to select transform + from next profile */ + renderState = getRenderingIntent(profileList[i1]); + + /* "middle" profiles use simulation transform */ + whichTrans = ColorTransform.Simulation; + } + + /* make the net transform */ + thisRasterTransform = mdl.createTransform(theTransforms); + } + + int srcTransferType = src.getTransferType(); + int dstTransferType = dest.getTransferType(); + if ((srcTransferType == DataBuffer.TYPE_FLOAT) || + (srcTransferType == DataBuffer.TYPE_DOUBLE) || + (dstTransferType == DataBuffer.TYPE_FLOAT) || + (dstTransferType == DataBuffer.TYPE_DOUBLE)) { + if (srcMinVals == null) { + getMinMaxValsFromProfiles(profileList[0], + profileList[nProfiles-1]); + } + /* color convert the raster */ + thisRasterTransform.colorConvert(src, dest, + srcMinVals, srcMaxVals, + dstMinVals, dstMaxVals); + } else { + /* color convert the raster */ + thisRasterTransform.colorConvert(src, dest); + } + + + return dest; + } + + /** + * Returns the bounding box of the destination, given this source. + * Note that this will be the same as the the bounding box of the + * source. + * @param src the source BufferedImage + * @return a Rectangle2D that is the bounding box + * of the destination, given the specified src + */ + public final Rectangle2D getBounds2D (BufferedImage src) { + return getBounds2D(src.getRaster()); + } + + /** + * Returns the bounding box of the destination, given this source. + * Note that this will be the same as the the bounding box of the + * source. + * @param src the source Raster + * @return a Rectangle2D that is the bounding box + * of the destination, given the specified src + */ + public final Rectangle2D getBounds2D (Raster src) { + /* return new Rectangle (src.getXOffset(), + src.getYOffset(), + src.getWidth(), src.getHeight()); */ + return src.getBounds(); + } + + /** + * Creates a zeroed destination image with the correct size and number of + * bands, given this source. + * @param src Source image for the filter operation. + * @param destCM ColorModel of the destination. If null, an + * appropriate ColorModel will be used. + * @return a BufferedImage with the correct size and + * number of bands from the specified src. + * @throws IllegalArgumentException if destCM is + * null and this ColorConvertOp was + * created without any ICC_Profile or + * ColorSpace defined for the destination + */ + public BufferedImage createCompatibleDestImage (BufferedImage src, + ColorModel destCM) { + ColorSpace cs = null;; + if (destCM == null) { + if (CSList == null) { + /* ICC case */ + int nProfiles = profileList.length; + if (nProfiles == 0) { + throw new IllegalArgumentException( + "Destination ColorSpace is undefined"); + } + ICC_Profile destProfile = profileList[nProfiles - 1]; + cs = new ICC_ColorSpace(destProfile); + } else { + /* non-ICC case */ + int nSpaces = CSList.length; + cs = CSList[nSpaces - 1]; + } + } + return createCompatibleDestImage(src, destCM, cs); + } + + private BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel destCM, + ColorSpace destCS) { + BufferedImage image; + if (destCM == null) { + ColorModel srcCM = src.getColorModel(); + int nbands = destCS.getNumComponents(); + boolean hasAlpha = srcCM.hasAlpha(); + if (hasAlpha) { + nbands += 1; + } + int[] nbits = new int[nbands]; + for (int i = 0; i < nbands; i++) { + nbits[i] = 8; + } + destCM = new ComponentColorModel(destCS, nbits, hasAlpha, + srcCM.isAlphaPremultiplied(), + srcCM.getTransparency(), + DataBuffer.TYPE_BYTE); + } + int w = src.getWidth(); + int h = src.getHeight(); + image = new BufferedImage(destCM, + destCM.createCompatibleWritableRaster(w, h), + destCM.isAlphaPremultiplied(), null); + return image; + } + + + /** + * Creates a zeroed destination Raster with the correct size and number of + * bands, given this source. + * @param src the specified Raster + * @return a WritableRaster with the correct size and number + * of bands from the specified src + * @throws IllegalArgumentException if this ColorConvertOp + * was created without sufficient information to define the + * dst and src color spaces + */ + public WritableRaster createCompatibleDestRaster (Raster src) { + int ncomponents; + + if (CSList != null) { + /* non-ICC case */ + if (CSList.length != 2) { + throw new IllegalArgumentException( + "Destination ColorSpace is undefined"); + } + ncomponents = CSList[1].getNumComponents(); + } else { + /* ICC case */ + int nProfiles = profileList.length; + if (nProfiles < 2) { + throw new IllegalArgumentException( + "Destination ColorSpace is undefined"); + } + ncomponents = profileList[nProfiles-1].getNumComponents(); + } + + WritableRaster dest = + Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, + src.getWidth(), + src.getHeight(), + ncomponents, + new Point(src.getMinX(), src.getMinY())); + return dest; + } + + /** + * Returns the location of the destination point given a + * point in the source. If dstPt is non-null, + * it will be used to hold the return value. Note that + * for this class, the destination point will be the same + * as the source point. + * @param srcPt the specified source Point2D + * @param dstPt the destination Point2D + * @return dstPt after setting its location to be + * the same as srcPt + */ + public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) { + if (dstPt == null) { + dstPt = new Point2D.Float(); + } + dstPt.setLocation(srcPt.getX(), srcPt.getY()); + + return dstPt; + } + + + /** + * Returns the RenderingIntent from the specified ICC Profile. + */ + private int getRenderingIntent (ICC_Profile profile) { + byte[] header = profile.getData(ICC_Profile.icSigHead); + int index = ICC_Profile.icHdrRenderingIntent; + return (((header[index] & 0xff) << 24) | + ((header[index+1] & 0xff) << 16) | + ((header[index+2] & 0xff) << 8) | + (header[index+3] & 0xff)); + } + + /** + * Returns the rendering hints used by this op. + * @return the RenderingHints object of this + * ColorConvertOp + */ + public final RenderingHints getRenderingHints() { + return hints; + } + + private final BufferedImage nonICCBIFilter(BufferedImage src, + ColorSpace srcColorSpace, + BufferedImage dst, + ColorSpace dstColorSpace) { + + int w = src.getWidth(); + int h = src.getHeight(); + ICC_ColorSpace ciespace = + (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); + if (dst == null) { + dst = createCompatibleDestImage(src, null); + dstColorSpace = dst.getColorModel().getColorSpace(); + } else { + if ((h != dst.getHeight()) || (w != dst.getWidth())) { + throw new IllegalArgumentException( + "Width or height of BufferedImages do not match"); + } + } + Raster srcRas = src.getRaster(); + WritableRaster dstRas = dst.getRaster(); + ColorModel srcCM = src.getColorModel(); + ColorModel dstCM = dst.getColorModel(); + int srcNumComp = srcCM.getNumColorComponents(); + int dstNumComp = dstCM.getNumColorComponents(); + boolean dstHasAlpha = dstCM.hasAlpha(); + boolean needSrcAlpha = srcCM.hasAlpha() && dstHasAlpha; + ColorSpace[] list; + if ((CSList == null) && (profileList.length != 0)) { + /* possible non-ICC src, some profiles, possible non-ICC dst */ + boolean nonICCSrc, nonICCDst; + ICC_Profile srcProfile, dstProfile; + if (!(srcColorSpace instanceof ICC_ColorSpace)) { + nonICCSrc = true; + srcProfile = ciespace.getProfile(); + } else { + nonICCSrc = false; + srcProfile = ((ICC_ColorSpace) srcColorSpace).getProfile(); + } + if (!(dstColorSpace instanceof ICC_ColorSpace)) { + nonICCDst = true; + dstProfile = ciespace.getProfile(); + } else { + nonICCDst = false; + dstProfile = ((ICC_ColorSpace) dstColorSpace).getProfile(); + } + /* make a new transform if needed */ + if ((thisTransform == null) || (thisSrcProfile != srcProfile) || + (thisDestProfile != dstProfile) ) { + updateBITransform(srcProfile, dstProfile); + } + // process per scanline + float maxNum = 65535.0f; // use 16-bit precision in CMM + ColorSpace cs; + int iccSrcNumComp; + if (nonICCSrc) { + cs = ciespace; + iccSrcNumComp = 3; + } else { + cs = srcColorSpace; + iccSrcNumComp = srcNumComp; + } + float[] srcMinVal = new float[iccSrcNumComp]; + float[] srcInvDiffMinMax = new float[iccSrcNumComp]; + for (int i = 0; i < srcNumComp; i++) { + srcMinVal[i] = cs.getMinValue(i); + srcInvDiffMinMax[i] = maxNum / (cs.getMaxValue(i) - srcMinVal[i]); + } + int iccDstNumComp; + if (nonICCDst) { + cs = ciespace; + iccDstNumComp = 3; + } else { + cs = dstColorSpace; + iccDstNumComp = dstNumComp; + } + float[] dstMinVal = new float[iccDstNumComp]; + float[] dstDiffMinMax = new float[iccDstNumComp]; + for (int i = 0; i < dstNumComp; i++) { + dstMinVal[i] = cs.getMinValue(i); + dstDiffMinMax[i] = (cs.getMaxValue(i) - dstMinVal[i]) / maxNum; + } + float[] dstColor; + if (dstHasAlpha) { + int size = ((dstNumComp + 1) > 3) ? (dstNumComp + 1) : 3; + dstColor = new float[size]; + } else { + int size = (dstNumComp > 3) ? dstNumComp : 3; + dstColor = new float[size]; + } + short[] srcLine = new short[w * iccSrcNumComp]; + short[] dstLine = new short[w * iccDstNumComp]; + Object pixel; + float[] color; + float[] alpha = null; + if (needSrcAlpha) { + alpha = new float[w]; + } + int idx; + // process each scanline + for (int y = 0; y < h; y++) { + // convert src scanline + pixel = null; + color = null; + idx = 0; + for (int x = 0; x < w; x++) { + pixel = srcRas.getDataElements(x, y, pixel); + color = srcCM.getNormalizedComponents(pixel, color, 0); + if (needSrcAlpha) { + alpha[x] = color[srcNumComp]; + } + if (nonICCSrc) { + color = srcColorSpace.toCIEXYZ(color); + } + for (int i = 0; i < iccSrcNumComp; i++) { + srcLine[idx++] = (short) + ((color[i] - srcMinVal[i]) * srcInvDiffMinMax[i] + + 0.5f); + } + } + // color convert srcLine to dstLine + thisTransform.colorConvert(srcLine, dstLine); + // convert dst scanline + pixel = null; + idx = 0; + for (int x = 0; x < w; x++) { + for (int i = 0; i < iccDstNumComp; i++) { + dstColor[i] = ((float) (dstLine[idx++] & 0xffff)) * + dstDiffMinMax[i] + dstMinVal[i]; + } + if (nonICCDst) { + color = srcColorSpace.fromCIEXYZ(dstColor); + for (int i = 0; i < dstNumComp; i++) { + dstColor[i] = color[i]; + } + } + if (needSrcAlpha) { + dstColor[dstNumComp] = alpha[x]; + } else if (dstHasAlpha) { + dstColor[dstNumComp] = 1.0f; + } + pixel = dstCM.getDataElements(dstColor, 0, pixel); + dstRas.setDataElements(x, y, pixel); + } + } + } else { + /* possible non-ICC src, possible CSList, possible non-ICC dst */ + // process per pixel + int numCS; + if (CSList == null) { + numCS = 0; + } else { + numCS = CSList.length; + } + float[] dstColor; + if (dstHasAlpha) { + dstColor = new float[dstNumComp + 1]; + } else { + dstColor = new float[dstNumComp]; + } + Object spixel = null; + Object dpixel = null; + float[] color = null; + float[] tmpColor; + // process each pixel + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + spixel = srcRas.getDataElements(x, y, spixel); + color = srcCM.getNormalizedComponents(spixel, color, 0); + tmpColor = srcColorSpace.toCIEXYZ(color); + for (int i = 0; i < numCS; i++) { + tmpColor = CSList[i].fromCIEXYZ(tmpColor); + tmpColor = CSList[i].toCIEXYZ(tmpColor); + } + tmpColor = dstColorSpace.fromCIEXYZ(tmpColor); + for (int i = 0; i < dstNumComp; i++) { + dstColor[i] = tmpColor[i]; + } + if (needSrcAlpha) { + dstColor[dstNumComp] = color[srcNumComp]; + } else if (dstHasAlpha) { + dstColor[dstNumComp] = 1.0f; + } + dpixel = dstCM.getDataElements(dstColor, 0, dpixel); + dstRas.setDataElements(x, y, dpixel); + + } + } + } + + return dst; + } + + /* color convert a Raster - handles byte, ushort, int, short, float, + or double transferTypes */ + private final WritableRaster nonICCRasterFilter(Raster src, + WritableRaster dst) { + + if (CSList.length != 2) { + throw new IllegalArgumentException( + "Destination ColorSpace is undefined"); + } + if (src.getNumBands() != CSList[0].getNumComponents()) { + throw new IllegalArgumentException( + "Numbers of source Raster bands and source color space " + + "components do not match"); + } + if (dst == null) { + dst = createCompatibleDestRaster(src); + } else { + if (src.getHeight() != dst.getHeight() || + src.getWidth() != dst.getWidth()) { + throw new IllegalArgumentException( + "Width or height of Rasters do not match"); + } + if (dst.getNumBands() != CSList[1].getNumComponents()) { + throw new IllegalArgumentException( + "Numbers of destination Raster bands and destination " + + "color space components do not match"); + } + } + + if (srcMinVals == null) { + getMinMaxValsFromColorSpaces(CSList[0], CSList[1]); + } + + SampleModel srcSM = src.getSampleModel(); + SampleModel dstSM = dst.getSampleModel(); + boolean srcIsFloat, dstIsFloat; + int srcTransferType = src.getTransferType(); + int dstTransferType = dst.getTransferType(); + if ((srcTransferType == DataBuffer.TYPE_FLOAT) || + (srcTransferType == DataBuffer.TYPE_DOUBLE)) { + srcIsFloat = true; + } else { + srcIsFloat = false; + } + if ((dstTransferType == DataBuffer.TYPE_FLOAT) || + (dstTransferType == DataBuffer.TYPE_DOUBLE)) { + dstIsFloat = true; + } else { + dstIsFloat = false; + } + int w = src.getWidth(); + int h = src.getHeight(); + int srcNumBands = src.getNumBands(); + int dstNumBands = dst.getNumBands(); + float[] srcScaleFactor = null; + float[] dstScaleFactor = null; + if (!srcIsFloat) { + srcScaleFactor = new float[srcNumBands]; + for (int i = 0; i < srcNumBands; i++) { + if (srcTransferType == DataBuffer.TYPE_SHORT) { + srcScaleFactor[i] = (srcMaxVals[i] - srcMinVals[i]) / + 32767.0f; + } else { + srcScaleFactor[i] = (srcMaxVals[i] - srcMinVals[i]) / + ((float) ((1 << srcSM.getSampleSize(i)) - 1)); + } + } + } + if (!dstIsFloat) { + dstScaleFactor = new float[dstNumBands]; + for (int i = 0; i < dstNumBands; i++) { + if (dstTransferType == DataBuffer.TYPE_SHORT) { + dstScaleFactor[i] = 32767.0f / + (dstMaxVals[i] - dstMinVals[i]); + } else { + dstScaleFactor[i] = + ((float) ((1 << dstSM.getSampleSize(i)) - 1)) / + (dstMaxVals[i] - dstMinVals[i]); + } + } + } + int ys = src.getMinY(); + int yd = dst.getMinY(); + int xs, xd; + float sample; + float[] color = new float[srcNumBands]; + float[] tmpColor; + ColorSpace srcColorSpace = CSList[0]; + ColorSpace dstColorSpace = CSList[1]; + // process each pixel + for (int y = 0; y < h; y++, ys++, yd++) { + // get src scanline + xs = src.getMinX(); + xd = dst.getMinX(); + for (int x = 0; x < w; x++, xs++, xd++) { + for (int i = 0; i < srcNumBands; i++) { + sample = src.getSampleFloat(xs, ys, i); + if (!srcIsFloat) { + sample = sample * srcScaleFactor[i] + srcMinVals[i]; + } + color[i] = sample; + } + tmpColor = srcColorSpace.toCIEXYZ(color); + tmpColor = dstColorSpace.fromCIEXYZ(tmpColor); + for (int i = 0; i < dstNumBands; i++) { + sample = tmpColor[i]; + if (!dstIsFloat) { + sample = (sample - dstMinVals[i]) * dstScaleFactor[i]; + } + dst.setSample(xd, yd, i, sample); + } + } + } + return dst; + } + + private void getMinMaxValsFromProfiles(ICC_Profile srcProfile, + ICC_Profile dstProfile) { + int type = srcProfile.getColorSpaceType(); + int nc = srcProfile.getNumComponents(); + srcMinVals = new float[nc]; + srcMaxVals = new float[nc]; + setMinMax(type, nc, srcMinVals, srcMaxVals); + type = dstProfile.getColorSpaceType(); + nc = dstProfile.getNumComponents(); + dstMinVals = new float[nc]; + dstMaxVals = new float[nc]; + setMinMax(type, nc, dstMinVals, dstMaxVals); + } + + private void setMinMax(int type, int nc, float[] minVals, float[] maxVals) { + if (type == ColorSpace.TYPE_Lab) { + minVals[0] = 0.0f; // L + maxVals[0] = 100.0f; + minVals[1] = -128.0f; // a + maxVals[1] = 127.0f; + minVals[2] = -128.0f; // b + maxVals[2] = 127.0f; + } else if (type == ColorSpace.TYPE_XYZ) { + minVals[0] = minVals[1] = minVals[2] = 0.0f; // X, Y, Z + maxVals[0] = maxVals[1] = maxVals[2] = 1.0f + (32767.0f/ 32768.0f); + } else { + for (int i = 0; i < nc; i++) { + minVals[i] = 0.0f; + maxVals[i] = 1.0f; + } + } + } + + private void getMinMaxValsFromColorSpaces(ColorSpace srcCspace, + ColorSpace dstCspace) { + int nc = srcCspace.getNumComponents(); + srcMinVals = new float[nc]; + srcMaxVals = new float[nc]; + for (int i = 0; i < nc; i++) { + srcMinVals[i] = srcCspace.getMinValue(i); + srcMaxVals[i] = srcCspace.getMaxValue(i); + } + nc = dstCspace.getNumComponents(); + dstMinVals = new float[nc]; + dstMaxVals = new float[nc]; + for (int i = 0; i < nc; i++) { + dstMinVals[i] = dstCspace.getMinValue(i); + dstMaxVals[i] = dstCspace.getMaxValue(i); + } + } + +} diff --git a/jdk/src/share/classes/java/awt/image/ComponentSampleModel.java b/jdk/src/share/classes/java/awt/image/ComponentSampleModel.java new file mode 100644 index 00000000000..ba0ebd47f5c --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/ComponentSampleModel.java @@ -0,0 +1,1202 @@ +/* + * Portions Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +import java.util.Arrays; + +/** + * This class represents image data which is stored such that each sample + * of a pixel occupies one data element of the DataBuffer. It stores the + * N samples which make up a pixel in N separate data array elements. + * Different bands may be in different banks of the DataBuffer. + * Accessor methods are provided so that image data can be manipulated + * directly. This class can support different kinds of interleaving, e.g. + * band interleaving, scanline interleaving, and pixel interleaving. + * Pixel stride is the number of data array elements between two samples + * for the same band on the same scanline. Scanline stride is the number + * of data array elements between a given sample and the corresponding sample + * in the same column of the next scanline. Band offsets denote the number + * of data array elements from the first data array element of the bank + * of the DataBuffer holding each band to the first sample of the band. + * The bands are numbered from 0 to N-1. This class can represent image + * data for which each sample is an unsigned integral number which can be + * stored in 8, 16, or 32 bits (using DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT, + * respectively), data for which each sample is a signed integral number + * which can be stored in 16 bits (using DataBuffer.TYPE_SHORT), + * or data for which each sample is a signed float or double quantity + * (using DataBuffer.TYPE_FLOAT or + * DataBuffer.TYPE_DOUBLE, respectively). + * All samples of a given ComponentSampleModel + * are stored with the same precision. All strides and offsets must be + * non-negative. This class supports + * {@link DataBuffer#TYPE_BYTE TYPE_BYTE}, + * {@link DataBuffer#TYPE_USHORT TYPE_USHORT}, + * {@link DataBuffer#TYPE_SHORT TYPE_SHORT}, + * {@link DataBuffer#TYPE_INT TYPE_INT}, + * {@link DataBuffer#TYPE_FLOAT TYPE_FLOAT}, + * {@link DataBuffer#TYPE_DOUBLE TYPE_DOUBLE}, + * @see java.awt.image.PixelInterleavedSampleModel + * @see java.awt.image.BandedSampleModel + */ + +public class ComponentSampleModel extends SampleModel +{ + /** Offsets for all bands in data array elements. */ + protected int bandOffsets[]; + + /** Index for each bank storing a band of image data. */ + protected int[] bankIndices; + + /** + * The number of bands in this + * ComponentSampleModel. + */ + protected int numBands = 1; + + /** + * The number of banks in this + * ComponentSampleModel. + */ + protected int numBanks = 1; + + /** + * Line stride (in data array elements) of the region of image + * data described by this ComponentSampleModel. + */ + protected int scanlineStride; + + /** Pixel stride (in data array elements) of the region of image + * data described by this ComponentSampleModel. + */ + protected int pixelStride; + + static private native void initIDs(); + static { + ColorModel.loadLibraries(); + initIDs(); + } + + /** + * Constructs a ComponentSampleModel with the specified parameters. + * The number of bands will be given by the length of the bandOffsets array. + * All bands will be stored in the first bank of the DataBuffer. + * @param dataType the data type for storing samples + * @param w the width (in pixels) of the region of + * image data described + * @param h the height (in pixels) of the region of + * image data described + * @param pixelStride the pixel stride of the region of image + * data described + * @param scanlineStride the line stride of the region of image + * data described + * @param bandOffsets the offsets of all bands + * @throws IllegalArgumentException if w or + * h is not greater than 0 + * @throws IllegalArgumentException if pixelStride + * is less than 0 + * @throws IllegalArgumentException if scanlineStride + * is less than 0 + * @throws IllegalArgumentException if numBands + * is less than 1 + * @throws IllegalArgumentException if the product of w + * and h is greater than + * Integer.MAX_VALUE + * @throws IllegalArgumentException if dataType is not + * one of the supported data types + */ + public ComponentSampleModel(int dataType, + int w, int h, + int pixelStride, + int scanlineStride, + int bandOffsets[]) { + super(dataType, w, h, bandOffsets.length); + this.dataType = dataType; + this.pixelStride = pixelStride; + this.scanlineStride = scanlineStride; + this.bandOffsets = (int[])bandOffsets.clone(); + numBands = bandOffsets.length; + if (pixelStride < 0) { + throw new IllegalArgumentException("Pixel stride must be >= 0"); + } + // TODO - bug 4296691 - remove this check + if (scanlineStride < 0) { + throw new IllegalArgumentException("Scanline stride must be >= 0"); + } + if (numBands < 1) { + throw new IllegalArgumentException("Must have at least one band."); + } + if ((dataType < DataBuffer.TYPE_BYTE) || + (dataType > DataBuffer.TYPE_DOUBLE)) { + throw new IllegalArgumentException("Unsupported dataType."); + } + bankIndices = new int[numBands]; + for (int i=0; iw or + * h is not greater than 0 + * @throws IllegalArgumentException if pixelStride + * is less than 0 + * @throws IllegalArgumentException if scanlineStride + * is less than 0 + * @throws IllegalArgumentException if the length of + * bankIndices does not equal the length of + * bankOffsets + * @throws IllegalArgumentException if any of the bank indices + * of bandIndices is less than 0 + * @throws IllegalArgumentException if dataType is not + * one of the supported data types + */ + public ComponentSampleModel(int dataType, + int w, int h, + int pixelStride, + int scanlineStride, + int bankIndices[], + int bandOffsets[]) { + super(dataType, w, h, bandOffsets.length); + this.dataType = dataType; + this.pixelStride = pixelStride; + this.scanlineStride = scanlineStride; + this.bandOffsets = (int[])bandOffsets.clone(); + this.bankIndices = (int[]) bankIndices.clone(); + if (pixelStride < 0) { + throw new IllegalArgumentException("Pixel stride must be >= 0"); + } + // TODO - bug 4296691 - remove this check + if (scanlineStride < 0) { + throw new IllegalArgumentException("Scanline stride must be >= 0"); + } + if ((dataType < DataBuffer.TYPE_BYTE) || + (dataType > DataBuffer.TYPE_DOUBLE)) { + throw new IllegalArgumentException("Unsupported dataType."); + } + int maxBank = bankIndices[0]; + if (maxBank < 0) { + throw new IllegalArgumentException("Index of bank 0 is less than "+ + "0 ("+maxBank+")"); + } + for (int i=1; i < bankIndices.length; i++) { + if (bankIndices[i] > maxBank) { + maxBank = bankIndices[i]; + } + else if (bankIndices[i] < 0) { + throw new IllegalArgumentException("Index of bank "+i+ + " is less than 0 ("+ + maxBank+")"); + } + } + numBanks = maxBank+1; + numBands = bandOffsets.length; + if (bandOffsets.length != bankIndices.length) { + throw new IllegalArgumentException("Length of bandOffsets must "+ + "equal length of bankIndices."); + } + } + + /** + * Returns the size of the data buffer (in data elements) needed + * for a data buffer that matches this ComponentSampleModel. + */ + private long getBufferSize() { + int maxBandOff=bandOffsets[0]; + for (int i=1; i= 0) + size += maxBandOff+1; + if (pixelStride > 0) + size += pixelStride * (width-1); + if (scanlineStride > 0) + size += scanlineStride*(height-1); + return size; + } + + /** + * Preserves band ordering with new step factor... + */ + int []orderBands(int orig[], int step) { + int map[] = new int[orig.length]; + int ret[] = new int[orig.length]; + + for (int i=0; i orig[map[j]]) { + index = j; + } + } + ret[map[index]] = i*step; + map[index] = map[i]; + } + return ret; + } + + /** + * Creates a new ComponentSampleModel with the specified + * width and height. The new SampleModel will have the same + * number of bands, storage data type, interleaving scheme, and + * pixel stride as this SampleModel. + * @param w the width of the resulting SampleModel + * @param h the height of the resulting SampleModel + * @return a new ComponentSampleModel with the specified size + * @throws IllegalArgumentException if w or + * h is not greater than 0 + */ + public SampleModel createCompatibleSampleModel(int w, int h) { + SampleModel ret=null; + long size; + int minBandOff=bandOffsets[0]; + int maxBandOff=bandOffsets[0]; + for (int i=1; i lStride) { + if (pStride > bStride) { + if (lStride > bStride) { // pix > line > band + bandOff = new int[bandOffsets.length]; + for (int i=0; i band > line + bandOff = orderBands(bandOffsets,lStride*h); + pStride = bands*lStride*h; + } + } else { // band > pix > line + pStride = lStride*h; + bandOff = orderBands(bandOffsets,pStride*w); + } + } else { + if (pStride > bStride) { // line > pix > band + bandOff = new int[bandOffsets.length]; + for (int i=0; i bStride) { // line > band > pix + bandOff = orderBands(bandOffsets,pStride*w); + lStride = bands*pStride*w; + } else { // band > line > pix + lStride = pStride*w; + bandOff = orderBands(bandOffsets,lStride*h); + } + } + } + + // make sure we make room for negative offsets... + int base = 0; + if (scanlineStride < 0) { + base += lStride*h; + lStride *= -1; + } + if (pixelStride < 0) { + base += pStride*w; + pStride *= -1; + } + + for (int i=0; iComponentSampleModel + * @return a ComponentSampleModel created with a subset + * of bands from this ComponentSampleModel. + */ + public SampleModel createSubsetSampleModel(int bands[]) { + if (bands.length > bankIndices.length) + throw new RasterFormatException("There are only " + + bankIndices.length + + " bands"); + int newBankIndices[] = new int[bands.length]; + int newBandOffsets[] = new int[bands.length]; + + for (int i=0; iDataBuffer that corresponds to this + * ComponentSampleModel. + * The DataBuffer object's data type, number of banks, + * and size are be consistent with this ComponentSampleModel. + * @return a DataBuffer whose data type, number of banks + * and size are consistent with this + * ComponentSampleModel. + */ + public DataBuffer createDataBuffer() { + DataBuffer dataBuffer = null; + + int size = (int)getBufferSize(); + switch (dataType) { + case DataBuffer.TYPE_BYTE: + dataBuffer = new DataBufferByte(size, numBanks); + break; + case DataBuffer.TYPE_USHORT: + dataBuffer = new DataBufferUShort(size, numBanks); + break; + case DataBuffer.TYPE_SHORT: + dataBuffer = new DataBufferShort(size, numBanks); + break; + case DataBuffer.TYPE_INT: + dataBuffer = new DataBufferInt(size, numBanks); + break; + case DataBuffer.TYPE_FLOAT: + dataBuffer = new DataBufferFloat(size, numBanks); + break; + case DataBuffer.TYPE_DOUBLE: + dataBuffer = new DataBufferDouble(size, numBanks); + break; + } + + return dataBuffer; + } + + + /** Gets the offset for the first band of pixel (x,y). + * A sample of the first band can be retrieved from a + * DataBuffer + * data with a ComponentSampleModel + * csm as + *

+     *        data.getElem(csm.getOffset(x, y));
+     * 
+ * @param x the X location of the pixel + * @param y the Y location of the pixel + * @return the offset for the first band of the specified pixel. + */ + public int getOffset(int x, int y) { + int offset = y*scanlineStride + x*pixelStride + bandOffsets[0]; + return offset; + } + + /** Gets the offset for band b of pixel (x,y). + * A sample of band b can be retrieved from a + * DataBuffer data + * with a ComponentSampleModel csm as + *
+     *       data.getElem(csm.getOffset(x, y, b));
+     * 
+ * @param x the X location of the specified pixel + * @param y the Y location of the specified pixel + * @param b the specified band + * @return the offset for the specified band of the specified pixel. + */ + public int getOffset(int x, int y, int b) { + int offset = y*scanlineStride + x*pixelStride + bandOffsets[b]; + return offset; + } + + /** Returns the number of bits per sample for all bands. + * @return an array containing the number of bits per sample + * for all bands, where each element in the array + * represents a band. + */ + public final int[] getSampleSize() { + int sampleSize[] = new int [numBands]; + int sizeInBits = getSampleSize(0); + + for (int i=0; iComponentSampleModel. + */ + public final int getScanlineStride() { + return scanlineStride; + } + + /** Returns the pixel stride of this ComponentSampleModel. + * @return the pixel stride of this ComponentSampleModel. + */ + public final int getPixelStride() { + return pixelStride; + } + + /** + * Returns the number of data elements needed to transfer a pixel + * with the + * {@link #getDataElements(int, int, Object, DataBuffer) } and + * {@link #setDataElements(int, int, Object, DataBuffer) } + * methods. + * For a ComponentSampleModel, this is identical to the + * number of bands. + * @return the number of data elements needed to transfer a pixel with + * the getDataElements and + * setDataElements methods. + * @see java.awt.image.SampleModel#getNumDataElements + * @see #getNumBands + */ + public final int getNumDataElements() { + return getNumBands(); + } + + /** + * Returns data for a single pixel in a primitive array of type + * TransferType. For a ComponentSampleModel, + * this is the same as the data type, and samples are returned + * one per array element. Generally, obj should + * be passed in as null, so that the Object + * is created automatically and is the right primitive data type. + *

+ * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is + * described by ComponentSampleModel csm1, + * to DataBuffer db2, whose storage layout + * is described by ComponentSampleModel csm2. + * The transfer is usually more efficient than using + * getPixel and setPixel. + *

+     *       ComponentSampleModel csm1, csm2;
+     *       DataBufferInt db1, db2;
+     *       csm2.setDataElements(x, y,
+     *                            csm1.getDataElements(x, y, null, db1), db2);
+     * 
+ * + * Using getDataElements and setDataElements + * to transfer between two DataBuffer/SampleModel + * pairs is legitimate if the SampleModel objects have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * If obj is not null, it should be a + * primitive array of type TransferType. + * Otherwise, a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException might be thrown if the + * coordinates are not in bounds, or if obj is not + * null and is not large enough to hold + * the pixel data. + * + * @param x the X coordinate of the pixel location + * @param y the Y coordinate of the pixel location + * @param obj if non-null, a primitive array + * in which to return the pixel data + * @param data the DataBuffer containing the image data + * @return the data of the specified pixel + * @see #setDataElements(int, int, Object, DataBuffer) + * + * @throws NullPointerException if data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if obj is too small to hold the ouput. + */ + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int type = getTransferType(); + int numDataElems = getNumDataElements(); + int pixelOffset = y*scanlineStride + x*pixelStride; + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] bdata; + + if (obj == null) + bdata = new byte[numDataElems]; + else + bdata = (byte[])obj; + + for (int i=0; iArrayIndexOutOfBoundsException might be thrown if + * the coordinates are not in bounds. + * @param x the X coordinate of the pixel location + * @param y the Y coordinate of the pixel location + * @param iArray If non-null, returns the samples in this array + * @param data The DataBuffer containing the image data + * @return the samples of the specified pixel. + * @see #setPixel(int, int, int[], DataBuffer) + * + * @throws NullPointerException if data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if iArray is too small to hold the output. + */ + public int[] getPixel(int x, int y, int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int pixels[]; + if (iArray != null) { + pixels = iArray; + } else { + pixels = new int [numBands]; + } + int pixelOffset = y*scanlineStride + x*pixelStride; + for (int i=0; iArrayIndexOutOfBoundsException might be thrown if + * the coordinates are not in bounds. + * @param x The X coordinate of the upper left pixel location + * @param y The Y coordinate of the upper left pixel location + * @param w The width of the pixel rectangle + * @param h The height of the pixel rectangle + * @param iArray If non-null, returns the samples in this array + * @param data The DataBuffer containing the image data + * @return the samples of the pixels within the specified region. + * @see #setPixels(int, int, int, int, int[], DataBuffer) + */ + public int[] getPixels(int x, int y, int w, int h, + int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int pixels[]; + if (iArray != null) { + pixels = iArray; + } else { + pixels = new int [w*h*numBands]; + } + int lineOffset = y*scanlineStride + x*pixelStride; + int srcOffset = 0; + + for (int i = 0; i < h; i++) { + int pixelOffset = lineOffset; + for (int j = 0; j < w; j++) { + for (int k=0; k < numBands; k++) { + pixels[srcOffset++] = + data.getElem(bankIndices[k], pixelOffset + bandOffsets[k]); + } + pixelOffset += pixelStride; + } + lineOffset += scanlineStride; + } + return pixels; + } + + /** + * Returns as int the sample in a specified band for the pixel + * located at (x,y). + * An ArrayIndexOutOfBoundsException might be thrown if + * the coordinates are not in bounds. + * @param x the X coordinate of the pixel location + * @param y the Y coordinate of the pixel location + * @param b the band to return + * @param data the DataBuffer containing the image data + * @return the sample in a specified band for the specified pixel + * @see #setSample(int, int, int, int, DataBuffer) + */ + public int getSample(int x, int y, int b, DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int sample = data.getElem(bankIndices[b], + y*scanlineStride + x*pixelStride + + bandOffsets[b]); + return sample; + } + + /** + * Returns the sample in a specified band + * for the pixel located at (x,y) as a float. + * An ArrayIndexOutOfBoundsException might be + * thrown if the coordinates are not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to return + * @param data The DataBuffer containing the image data + * @return a float value representing the sample in the specified + * band for the specified pixel. + */ + public float getSampleFloat(int x, int y, int b, DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + float sample = data.getElemFloat(bankIndices[b], + y*scanlineStride + x*pixelStride + + bandOffsets[b]); + return sample; + } + + /** + * Returns the sample in a specified band + * for a pixel located at (x,y) as a double. + * An ArrayIndexOutOfBoundsException might be + * thrown if the coordinates are not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to return + * @param data The DataBuffer containing the image data + * @return a double value representing the sample in the specified + * band for the specified pixel. + */ + public double getSampleDouble(int x, int y, int b, DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + double sample = data.getElemDouble(bankIndices[b], + y*scanlineStride + x*pixelStride + + bandOffsets[b]); + return sample; + } + + /** + * Returns the samples in a specified band for the specified rectangle + * of pixels in an int array, one sample per data array element. + * An ArrayIndexOutOfBoundsException might be thrown if + * the coordinates are not in bounds. + * @param x The X coordinate of the upper left pixel location + * @param y The Y coordinate of the upper left pixel location + * @param w the width of the pixel rectangle + * @param h the height of the pixel rectangle + * @param b the band to return + * @param iArray if non-null, returns the samples + * in this array + * @param data the DataBuffer containing the image data + * @return the samples in the specified band of the specified pixel + * @see #setSamples(int, int, int, int, int, int[], DataBuffer) + */ + public int[] getSamples(int x, int y, int w, int h, int b, + int iArray[], DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int samples[]; + if (iArray != null) { + samples = iArray; + } else { + samples = new int [w*h]; + } + int lineOffset = y*scanlineStride + x*pixelStride + bandOffsets[b]; + int srcOffset = 0; + + for (int i = 0; i < h; i++) { + int sampleOffset = lineOffset; + for (int j = 0; j < w; j++) { + samples[srcOffset++] = data.getElem(bankIndices[b], + sampleOffset); + sampleOffset += pixelStride; + } + lineOffset += scanlineStride; + } + return samples; + } + + /** + * Sets the data for a single pixel in the specified + * DataBuffer from a primitive array of type + * TransferType. For a ComponentSampleModel, + * this is the same as the data type, and samples are transferred + * one per array element. + *

+ * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is + * described by ComponentSampleModel csm1, + * to DataBuffer db2, whose storage layout + * is described by ComponentSampleModel csm2. + * The transfer is usually more efficient than using + * getPixel and setPixel. + *

+     *       ComponentSampleModel csm1, csm2;
+     *       DataBufferInt db1, db2;
+     *       csm2.setDataElements(x, y, csm1.getDataElements(x, y, null, db1),
+     *                            db2);
+     * 
+ * Using getDataElements and setDataElements + * to transfer between two DataBuffer/SampleModel pairs + * is legitimate if the SampleModel objects have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * A ClassCastException is thrown if obj is not + * a primitive array of type TransferType. + * An ArrayIndexOutOfBoundsException might be thrown if + * the coordinates are not in bounds, or if obj is not large + * enough to hold the pixel data. + * @param x the X coordinate of the pixel location + * @param y the Y coordinate of the pixel location + * @param obj a primitive array containing pixel data + * @param data the DataBuffer containing the image data + * @see #getDataElements(int, int, Object, DataBuffer) + */ + public void setDataElements(int x, int y, Object obj, DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int type = getTransferType(); + int numDataElems = getNumDataElements(); + int pixelOffset = y*scanlineStride + x*pixelStride; + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] barray = (byte[])obj; + + for (int i=0; iDataBuffer using an int array of + * samples for input. An ArrayIndexOutOfBoundsException + * might be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param iArray The input samples in an int array + * @param data The DataBuffer containing the image data + * @see #getPixel(int, int, int[], DataBuffer) + */ + public void setPixel(int x, int y, int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int pixelOffset = y*scanlineStride + x*pixelStride; + for (int i=0; iArrayIndexOutOfBoundsException might be thrown if the + * coordinates are not in bounds. + * @param x The X coordinate of the upper left pixel location + * @param y The Y coordinate of the upper left pixel location + * @param w The width of the pixel rectangle + * @param h The height of the pixel rectangle + * @param iArray The input samples in an int array + * @param data The DataBuffer containing the image data + * @see #getPixels(int, int, int, int, int[], DataBuffer) + */ + public void setPixels(int x, int y, int w, int h, + int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int lineOffset = y*scanlineStride + x*pixelStride; + int srcOffset = 0; + + for (int i = 0; i < h; i++) { + int pixelOffset = lineOffset; + for (int j = 0; j < w; j++) { + for (int k=0; k < numBands; k++) { + data.setElem(bankIndices[k], pixelOffset + bandOffsets[k], + iArray[srcOffset++]); + } + pixelOffset += pixelStride; + } + lineOffset += scanlineStride; + } + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using an int for input. + * An ArrayIndexOutOfBoundsException might be thrown if the + * coordinates are not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b the band to set + * @param s the input sample as an int + * @param data the DataBuffer containing the image data + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, int s, + DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + data.setElem(bankIndices[b], + y*scanlineStride + x*pixelStride + bandOffsets[b], s); + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using a float for input. + * An ArrayIndexOutOfBoundsException might be thrown if + * the coordinates are not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to set + * @param s The input sample as a float + * @param data The DataBuffer containing the image data + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, + float s , + DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + data.setElemFloat(bankIndices[b], + y*scanlineStride + x*pixelStride + bandOffsets[b], + s); + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using a double for input. + * An ArrayIndexOutOfBoundsException might be thrown if + * the coordinates are not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to set + * @param s The input sample as a double + * @param data The DataBuffer containing the image data + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, + double s, + DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + data.setElemDouble(bankIndices[b], + y*scanlineStride + x*pixelStride + bandOffsets[b], + s); + } + + /** + * Sets the samples in the specified band for the specified rectangle + * of pixels from an int array containing one sample per data array element. + * An ArrayIndexOutOfBoundsException might be thrown if the + * coordinates are not in bounds. + * @param x The X coordinate of the upper left pixel location + * @param y The Y coordinate of the upper left pixel location + * @param w The width of the pixel rectangle + * @param h The height of the pixel rectangle + * @param b The band to set + * @param iArray The input samples in an int array + * @param data The DataBuffer containing the image data + * @see #getSamples(int, int, int, int, int, int[], DataBuffer) + */ + public void setSamples(int x, int y, int w, int h, int b, + int iArray[], DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int lineOffset = y*scanlineStride + x*pixelStride + bandOffsets[b]; + int srcOffset = 0; + + for (int i = 0; i < h; i++) { + int sampleOffset = lineOffset; + for (int j = 0; j < w; j++) { + data.setElem(bankIndices[b], sampleOffset, iArray[srcOffset++]); + sampleOffset += pixelStride; + } + lineOffset += scanlineStride; + } + } + + public boolean equals(Object o) { + if ((o == null) || !(o instanceof ComponentSampleModel)) { + return false; + } + + ComponentSampleModel that = (ComponentSampleModel)o; + return this.width == that.width && + this.height == that.height && + this.numBands == that.numBands && + this.dataType == that.dataType && + Arrays.equals(this.bandOffsets, that.bandOffsets) && + Arrays.equals(this.bankIndices, that.bankIndices) && + this.numBands == that.numBands && + this.numBanks == that.numBanks && + this.scanlineStride == that.scanlineStride && + this.pixelStride == that.pixelStride; + } + + // If we implement equals() we must also implement hashCode + public int hashCode() { + int hash = 0; + hash = width; + hash <<= 8; + hash ^= height; + hash <<= 8; + hash ^= numBands; + hash <<= 8; + hash ^= dataType; + hash <<= 8; + for (int i = 0; i < bandOffsets.length; i++) { + hash ^= bandOffsets[i]; + hash <<= 8; + } + for (int i = 0; i < bankIndices.length; i++) { + hash ^= bankIndices[i]; + hash <<= 8; + } + hash ^= numBands; + hash <<= 8; + hash ^= numBanks; + hash <<= 8; + hash ^= scanlineStride; + hash <<= 8; + hash ^= pixelStride; + return hash; + } +} diff --git a/jdk/src/share/classes/java/awt/image/DataBuffer.java b/jdk/src/share/classes/java/awt/image/DataBuffer.java new file mode 100644 index 00000000000..a2cbfbc19f5 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/DataBuffer.java @@ -0,0 +1,535 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +import sun.java2d.StateTrackable.State; +import static sun.java2d.StateTrackable.State.*; +import sun.java2d.StateTrackableDelegate; + +import sun.awt.image.SunWritableRaster; + +/** + * This class exists to wrap one or more data arrays. Each data array in + * the DataBuffer is referred to as a bank. Accessor methods for getting + * and setting elements of the DataBuffer's banks exist with and without + * a bank specifier. The methods without a bank specifier use the default 0th + * bank. The DataBuffer can optionally take an offset per bank, so that + * data in an existing array can be used even if the interesting data + * doesn't start at array location zero. Getting or setting the 0th + * element of a bank, uses the (0+offset)th element of the array. The + * size field specifies how much of the data array is available for + * use. Size + offset for a given bank should never be greater + * than the length of the associated data array. The data type of + * a data buffer indicates the type of the data array(s) and may also + * indicate additional semantics, e.g. storing unsigned 8-bit data + * in elements of a byte array. The data type may be TYPE_UNDEFINED + * or one of the types defined below. Other types may be added in + * the future. Generally, an object of class DataBuffer will be cast down + * to one of its data type specific subclasses to access data type specific + * methods for improved performance. Currently, the Java 2D(tm) API + * image classes use TYPE_BYTE, TYPE_USHORT, TYPE_INT, TYPE_SHORT, + * TYPE_FLOAT, and TYPE_DOUBLE DataBuffers to store image data. + * @see java.awt.image.Raster + * @see java.awt.image.SampleModel + */ +public abstract class DataBuffer { + + /** Tag for unsigned byte data. */ + public static final int TYPE_BYTE = 0; + + /** Tag for unsigned short data. */ + public static final int TYPE_USHORT = 1; + + /** Tag for signed short data. Placeholder for future use. */ + public static final int TYPE_SHORT = 2; + + /** Tag for int data. */ + public static final int TYPE_INT = 3; + + /** Tag for float data. Placeholder for future use. */ + public static final int TYPE_FLOAT = 4; + + /** Tag for double data. Placeholder for future use. */ + public static final int TYPE_DOUBLE = 5; + + /** Tag for undefined data. */ + public static final int TYPE_UNDEFINED = 32; + + /** The data type of this DataBuffer. */ + protected int dataType; + + /** The number of banks in this DataBuffer. */ + protected int banks; + + /** Offset into default (first) bank from which to get the first element. */ + protected int offset; + + /** Usable size of all banks. */ + protected int size; + + /** Offsets into all banks. */ + protected int offsets[]; + + /* The current StateTrackable state. */ + StateTrackableDelegate theTrackable; + + /** Size of the data types indexed by DataType tags defined above. */ + private static final int dataTypeSize[] = {8,16,16,32,32,64}; + + /** Returns the size (in bits) of the data type, given a datatype tag. + * @param type the value of one of the defined datatype tags + * @return the size of the data type + * @throws IllegalArgumentException if type is less than + * zero or greater than {@link #TYPE_DOUBLE} + */ + public static int getDataTypeSize(int type) { + if (type < TYPE_BYTE || type > TYPE_DOUBLE) { + throw new IllegalArgumentException("Unknown data type "+type); + } + return dataTypeSize[type]; + } + + /** + * Constructs a DataBuffer containing one bank of the specified + * data type and size. + * + * @param dataType the data type of this DataBuffer + * @param size the size of the banks + */ + protected DataBuffer(int dataType, int size) { + this(UNTRACKABLE, dataType, size); + } + + /** + * Constructs a DataBuffer containing one bank of the specified + * data type and size with the indicated initial {@link State State}. + * + * @param initialState the initial {@link State State} state of the data + * @param dataType the data type of this DataBuffer + * @param size the size of the banks + * @since 1.7 + */ + DataBuffer(State initialState, + int dataType, int size) + { + this.theTrackable = StateTrackableDelegate.createInstance(initialState); + this.dataType = dataType; + this.banks = 1; + this.size = size; + this.offset = 0; + this.offsets = new int[1]; // init to 0 by new + } + + /** + * Constructs a DataBuffer containing the specified number of + * banks. Each bank has the specified size and an offset of 0. + * + * @param dataType the data type of this DataBuffer + * @param size the size of the banks + * @param numBanks the number of banks in this + * DataBuffer + */ + protected DataBuffer(int dataType, int size, int numBanks) { + this(UNTRACKABLE, dataType, size, numBanks); + } + + /** + * Constructs a DataBuffer containing the specified number of + * banks with the indicated initial {@link State State}. + * Each bank has the specified size and an offset of 0. + * + * @param initialState the initial {@link State State} state of the data + * @param dataType the data type of this DataBuffer + * @param size the size of the banks + * @param numBanks the number of banks in this + * DataBuffer + * @since 1.7 + */ + DataBuffer(State initialState, + int dataType, int size, int numBanks) + { + this.theTrackable = StateTrackableDelegate.createInstance(initialState); + this.dataType = dataType; + this.banks = numBanks; + this.size = size; + this.offset = 0; + this.offsets = new int[banks]; // init to 0 by new + } + + /** + * Constructs a DataBuffer that contains the specified number + * of banks. Each bank has the specified datatype, size and offset. + * + * @param dataType the data type of this DataBuffer + * @param size the size of the banks + * @param numBanks the number of banks in this + * DataBuffer + * @param offset the offset for each bank + */ + protected DataBuffer(int dataType, int size, int numBanks, int offset) { + this(UNTRACKABLE, dataType, size, numBanks, offset); + } + + /** + * Constructs a DataBuffer that contains the specified number + * of banks with the indicated initial {@link State State}. + * Each bank has the specified datatype, size and offset. + * + * @param initialState the initial {@link State State} state of the data + * @param dataType the data type of this DataBuffer + * @param size the size of the banks + * @param numBanks the number of banks in this + * DataBuffer + * @param offset the offset for each bank + * @since 1.7 + */ + DataBuffer(State initialState, + int dataType, int size, int numBanks, int offset) + { + this.theTrackable = StateTrackableDelegate.createInstance(initialState); + this.dataType = dataType; + this.banks = numBanks; + this.size = size; + this.offset = offset; + this.offsets = new int[numBanks]; + for (int i = 0; i < numBanks; i++) { + this.offsets[i] = offset; + } + } + + /** + * Constructs a DataBuffer which contains the specified number + * of banks. Each bank has the specified datatype and size. The + * offset for each bank is specified by its respective entry in + * the offsets array. + * + * @param dataType the data type of this DataBuffer + * @param size the size of the banks + * @param numBanks the number of banks in this + * DataBuffer + * @param offsets an array containing an offset for each bank. + * @throws ArrayIndexOutOfBoundsException if numBanks + * does not equal the length of offsets + */ + protected DataBuffer(int dataType, int size, int numBanks, int offsets[]) { + this(UNTRACKABLE, dataType, size, numBanks, offsets); + } + + /** + * Constructs a DataBuffer which contains the specified number + * of banks with the indicated initial {@link State State}. + * Each bank has the specified datatype and size. The + * offset for each bank is specified by its respective entry in + * the offsets array. + * + * @param initialState the initial {@link State State} state of the data + * @param dataType the data type of this DataBuffer + * @param size the size of the banks + * @param numBanks the number of banks in this + * DataBuffer + * @param offsets an array containing an offset for each bank. + * @throws ArrayIndexOutOfBoundsException if numBanks + * does not equal the length of offsets + * @since 1.7 + */ + DataBuffer(State initialState, + int dataType, int size, int numBanks, int offsets[]) + { + if (numBanks != offsets.length) { + throw new ArrayIndexOutOfBoundsException("Number of banks" + + " does not match number of bank offsets"); + } + this.theTrackable = StateTrackableDelegate.createInstance(initialState); + this.dataType = dataType; + this.banks = numBanks; + this.size = size; + this.offset = offsets[0]; + this.offsets = (int[])offsets.clone(); + } + + /** Returns the data type of this DataBuffer. + * @return the data type of this DataBuffer. + */ + public int getDataType() { + return dataType; + } + + /** Returns the size (in array elements) of all banks. + * @return the size of all banks. + */ + public int getSize() { + return size; + } + + /** Returns the offset of the default bank in array elements. + * @return the offset of the default bank. + */ + public int getOffset() { + return offset; + } + + /** Returns the offsets (in array elements) of all the banks. + * @return the offsets of all banks. + */ + public int[] getOffsets() { + return (int[])offsets.clone(); + } + + /** Returns the number of banks in this DataBuffer. + * @return the number of banks. + */ + public int getNumBanks() { + return banks; + } + + /** + * Returns the requested data array element from the first (default) bank + * as an integer. + * @param i the index of the requested data array element + * @return the data array element at the specified index. + * @see #setElem(int, int) + * @see #setElem(int, int, int) + */ + public int getElem(int i) { + return getElem(0,i); + } + + /** + * Returns the requested data array element from the specified bank + * as an integer. + * @param bank the specified bank + * @param i the index of the requested data array element + * @return the data array element at the specified index from the + * specified bank at the specified index. + * @see #setElem(int, int) + * @see #setElem(int, int, int) + */ + public abstract int getElem(int bank, int i); + + /** + * Sets the requested data array element in the first (default) bank + * from the given integer. + * @param i the specified index into the data array + * @param val the data to set the element at the specified index in + * the data array + * @see #getElem(int) + * @see #getElem(int, int) + */ + public void setElem(int i, int val) { + setElem(0,i,val); + } + + /** + * Sets the requested data array element in the specified bank + * from the given integer. + * @param bank the specified bank + * @param i the specified index into the data array + * @param val the data to set the element in the specified bank + * at the specified index in the data array + * @see #getElem(int) + * @see #getElem(int, int) + */ + public abstract void setElem(int bank, int i, int val); + + /** + * Returns the requested data array element from the first (default) bank + * as a float. The implementation in this class is to cast getElem(i) + * to a float. Subclasses may override this method if another + * implementation is needed. + * @param i the index of the requested data array element + * @return a float value representing the data array element at the + * specified index. + * @see #setElemFloat(int, float) + * @see #setElemFloat(int, int, float) + */ + public float getElemFloat(int i) { + return (float)getElem(i); + } + + /** + * Returns the requested data array element from the specified bank + * as a float. The implementation in this class is to cast + * {@link #getElem(int, int)} + * to a float. Subclasses can override this method if another + * implementation is needed. + * @param bank the specified bank + * @param i the index of the requested data array element + * @return a float value representing the data array element from the + * specified bank at the specified index. + * @see #setElemFloat(int, float) + * @see #setElemFloat(int, int, float) + */ + public float getElemFloat(int bank, int i) { + return (float)getElem(bank,i); + } + + /** + * Sets the requested data array element in the first (default) bank + * from the given float. The implementation in this class is to cast + * val to an int and call {@link #setElem(int, int)}. Subclasses + * can override this method if another implementation is needed. + * @param i the specified index + * @param val the value to set the element at the specified index in + * the data array + * @see #getElemFloat(int) + * @see #getElemFloat(int, int) + */ + public void setElemFloat(int i, float val) { + setElem(i,(int)val); + } + + /** + * Sets the requested data array element in the specified bank + * from the given float. The implementation in this class is to cast + * val to an int and call {@link #setElem(int, int)}. Subclasses can + * override this method if another implementation is needed. + * @param bank the specified bank + * @param i the specified index + * @param val the value to set the element in the specified bank at + * the specified index in the data array + * @see #getElemFloat(int) + * @see #getElemFloat(int, int) + */ + public void setElemFloat(int bank, int i, float val) { + setElem(bank,i,(int)val); + } + + /** + * Returns the requested data array element from the first (default) bank + * as a double. The implementation in this class is to cast + * {@link #getElem(int)} + * to a double. Subclasses can override this method if another + * implementation is needed. + * @param i the specified index + * @return a double value representing the element at the specified + * index in the data array. + * @see #setElemDouble(int, double) + * @see #setElemDouble(int, int, double) + */ + public double getElemDouble(int i) { + return (double)getElem(i); + } + + /** + * Returns the requested data array element from the specified bank as + * a double. The implementation in this class is to cast getElem(bank, i) + * to a double. Subclasses may override this method if another + * implementation is needed. + * @param bank the specified bank + * @param i the specified index + * @return a double value representing the element from the specified + * bank at the specified index in the data array. + * @see #setElemDouble(int, double) + * @see #setElemDouble(int, int, double) + */ + public double getElemDouble(int bank, int i) { + return (double)getElem(bank,i); + } + + /** + * Sets the requested data array element in the first (default) bank + * from the given double. The implementation in this class is to cast + * val to an int and call {@link #setElem(int, int)}. Subclasses can + * override this method if another implementation is needed. + * @param i the specified index + * @param val the value to set the element at the specified index + * in the data array + * @see #getElemDouble(int) + * @see #getElemDouble(int, int) + */ + public void setElemDouble(int i, double val) { + setElem(i,(int)val); + } + + /** + * Sets the requested data array element in the specified bank + * from the given double. The implementation in this class is to cast + * val to an int and call {@link #setElem(int, int)}. Subclasses can + * override this method if another implementation is needed. + * @param bank the specified bank + * @param i the specified index + * @param val the value to set the element in the specified bank + * at the specified index of the data array + * @see #getElemDouble(int) + * @see #getElemDouble(int, int) + */ + public void setElemDouble(int bank, int i, double val) { + setElem(bank,i,(int)val); + } + + static int[] toIntArray(Object obj) { + if (obj instanceof int[]) { + return (int[])obj; + } else if (obj == null) { + return null; + } else if (obj instanceof short[]) { + short sdata[] = (short[])obj; + int idata[] = new int[sdata.length]; + for (int i = 0; i < sdata.length; i++) { + idata[i] = (int)sdata[i] & 0xffff; + } + return idata; + } else if (obj instanceof byte[]) { + byte bdata[] = (byte[])obj; + int idata[] = new int[bdata.length]; + for (int i = 0; i < bdata.length; i++) { + idata[i] = 0xff & (int)bdata[i]; + } + return idata; + } + return null; + } + + static { + SunWritableRaster.setDataStealer(new SunWritableRaster.DataStealer() { + public byte[] getData(DataBufferByte dbb, int bank) { + return dbb.bankdata[bank]; + } + + public short[] getData(DataBufferUShort dbus, int bank) { + return dbus.bankdata[bank]; + } + + public int[] getData(DataBufferInt dbi, int bank) { + return dbi.bankdata[bank]; + } + + public StateTrackableDelegate getTrackable(DataBuffer db) { + return db.theTrackable; + } + }); + } +} diff --git a/jdk/src/share/classes/java/awt/image/DataBufferByte.java b/jdk/src/share/classes/java/awt/image/DataBufferByte.java new file mode 100644 index 00000000000..2014fe705d8 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/DataBufferByte.java @@ -0,0 +1,286 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +import static sun.java2d.StateTrackable.State.*; + +/** + * This class extends DataBuffer and stores data internally as bytes. + * Values stored in the byte array(s) of this DataBuffer are treated as + * unsigned values. + *

+ * + * Note that some implementations may function more efficiently + * if they can maintain control over how the data for an image is + * stored. + * For example, optimizations such as caching an image in video + * memory require that the implementation track all modifications + * to that data. + * Other implementations may operate better if they can store the + * data in locations other than a Java array. + * To maintain optimum compatibility with various optimizations + * it is best to avoid constructors and methods which expose the + * underlying storage as a Java array, as noted below in the + * documentation for those methods. + * + */ +public final class DataBufferByte extends DataBuffer +{ + /** The default data bank. */ + byte data[]; + + /** All data banks */ + byte bankdata[][]; + + /** + * Constructs a byte-based DataBuffer with a single bank and the + * specified size. + * + * @param size The size of the DataBuffer. + */ + public DataBufferByte(int size) { + super(STABLE, TYPE_BYTE, size); + data = new byte[size]; + bankdata = new byte[1][]; + bankdata[0] = data; + } + + /** + * Constructs a byte based DataBuffer with the specified number of + * banks all of which are the specified size. + * + * @param size The size of the banks in the DataBuffer. + * @param numBanks The number of banks in the aDataBuffer. + */ + public DataBufferByte(int size, int numBanks) { + super(STABLE, TYPE_BYTE, size, numBanks); + bankdata = new byte[numBanks][]; + for (int i= 0; i < numBanks; i++) { + bankdata[i] = new byte[size]; + } + data = bankdata[0]; + } + + /** + * Constructs a byte-based DataBuffer with a single bank using the + * specified array. + * Only the first size elements should be used by accessors of + * this DataBuffer. dataArray must be large enough to + * hold size elements. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The byte array for the DataBuffer. + * @param size The size of the DataBuffer bank. + */ + public DataBufferByte(byte dataArray[], int size) { + super(UNTRACKABLE, TYPE_BYTE, size); + data = dataArray; + bankdata = new byte[1][]; + bankdata[0] = data; + } + + /** + * Constructs a byte-based DataBuffer with a single bank using the + * specified array, size, and offset. dataArray must have at least + * offset + size elements. Only elements offset + * through offset + size - 1 + * should be used by accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The byte array for the DataBuffer. + * @param size The size of the DataBuffer bank. + * @param offset The offset into the dataArray. dataArray + * must have at least offset + size elements. + */ + public DataBufferByte(byte dataArray[], int size, int offset){ + super(UNTRACKABLE, TYPE_BYTE, size, 1, offset); + data = dataArray; + bankdata = new byte[1][]; + bankdata[0] = data; + } + + /** + * Constructs a byte-based DataBuffer with the specified arrays. + * The number of banks is equal to dataArray.length. + * Only the first size elements of each array should be used by + * accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The byte arrays for the DataBuffer. + * @param size The size of the banks in the DataBuffer. + */ + public DataBufferByte(byte dataArray[][], int size) { + super(UNTRACKABLE, TYPE_BYTE, size, dataArray.length); + bankdata = (byte[][]) dataArray.clone(); + data = bankdata[0]; + } + + /** + * Constructs a byte-based DataBuffer with the specified arrays, size, + * and offsets. + * The number of banks is equal to dataArray.length. Each array must + * be at least as large as size + the corresponding offset. + * There must be an entry in the offset array for each dataArray + * entry. For each bank, only elements offset through + * offset + size - 1 should be used by accessors of this + * DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The byte arrays for the DataBuffer. + * @param size The size of the banks in the DataBuffer. + * @param offsets The offsets into each array. + */ + public DataBufferByte(byte dataArray[][], int size, int offsets[]) { + super(UNTRACKABLE, TYPE_BYTE, size, dataArray.length, offsets); + bankdata = (byte[][]) dataArray.clone(); + data = bankdata[0]; + } + + /** + * Returns the default (first) byte data array. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @return The first byte data array. + */ + public byte[] getData() { + theTrackable.setUntrackable(); + return data; + } + + /** + * Returns the data array for the specified bank. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param bank The bank whose data array you want to get. + * @return The data array for the specified bank. + */ + public byte[] getData(int bank) { + theTrackable.setUntrackable(); + return bankdata[bank]; + } + + /** + * Returns the data arrays for all banks. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @return All of the data arrays. + */ + public byte[][] getBankData() { + theTrackable.setUntrackable(); + return (byte[][]) bankdata.clone(); + } + + /** + * 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 (int)(data[i+offset]) & 0xff; + } + + /** + * 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 (int)(bankdata[bank][i+offsets[bank]]) & 0xff; + } + + /** + * 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[i+offset] = (byte)val; + theTrackable.markDirty(); + } + + /** + * Sets the requested data array element in the specified bank + * from the given integer. + * @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][i+offsets[bank]] = (byte)val; + theTrackable.markDirty(); + } +} diff --git a/jdk/src/share/classes/java/awt/image/DataBufferInt.java b/jdk/src/share/classes/java/awt/image/DataBufferInt.java new file mode 100644 index 00000000000..71580a65b96 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/DataBufferInt.java @@ -0,0 +1,284 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +import static sun.java2d.StateTrackable.State.*; + +/** + * This class extends DataBuffer and stores data internally + * as integers. + *

+ * + * Note that some implementations may function more efficiently + * if they can maintain control over how the data for an image is + * stored. + * For example, optimizations such as caching an image in video + * memory require that the implementation track all modifications + * to that data. + * Other implementations may operate better if they can store the + * data in locations other than a Java array. + * To maintain optimum compatibility with various optimizations + * it is best to avoid constructors and methods which expose the + * underlying storage as a Java array as noted below in the + * documentation for those methods. + * + */ +public final class DataBufferInt extends DataBuffer +{ + /** The default data bank. */ + int data[]; + + /** All data banks */ + int bankdata[][]; + + /** + * Constructs an integer-based DataBuffer with a single bank + * and the specified size. + * + * @param size The size of the DataBuffer. + */ + public DataBufferInt(int size) { + super(STABLE, TYPE_INT, size); + data = new int[size]; + bankdata = new int[1][]; + bankdata[0] = data; + } + + /** + * Constructs an integer-based DataBuffer with the specified number of + * banks, all of which are the specified size. + * + * @param size The size of the banks in the DataBuffer. + * @param numBanks The number of banks in the aDataBuffer. + */ + public DataBufferInt(int size, int numBanks) { + super(STABLE, TYPE_INT, size, numBanks); + bankdata = new int[numBanks][]; + for (int i= 0; i < numBanks; i++) { + bankdata[i] = new int[size]; + } + data = bankdata[0]; + } + + /** + * Constructs an integer-based DataBuffer with a single bank using the + * specified array. + * Only the first size elements should be used by accessors of + * this DataBuffer. dataArray must be large enough to + * hold size elements. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The integer array for the DataBuffer. + * @param size The size of the DataBuffer bank. + */ + public DataBufferInt(int dataArray[], int size) { + super(UNTRACKABLE, TYPE_INT, size); + data = dataArray; + bankdata = new int[1][]; + bankdata[0] = data; + } + + /** + * Constructs an integer-based DataBuffer with a single bank using the + * specified array, size, and offset. dataArray must have at least + * offset + size elements. Only elements offset + * through offset + size - 1 + * should be used by accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The integer array for the DataBuffer. + * @param size The size of the DataBuffer bank. + * @param offset The offset into the dataArray. + */ + public DataBufferInt(int dataArray[], int size, int offset) { + super(UNTRACKABLE, TYPE_INT, size, 1, offset); + data = dataArray; + bankdata = new int[1][]; + bankdata[0] = data; + } + + /** + * Constructs an integer-based DataBuffer with the specified arrays. + * The number of banks will be equal to dataArray.length. + * Only the first size elements of each array should be used by + * accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The integer arrays for the DataBuffer. + * @param size The size of the banks in the DataBuffer. + */ + public DataBufferInt(int dataArray[][], int size) { + super(UNTRACKABLE, TYPE_INT, size, dataArray.length); + bankdata = (int [][]) dataArray.clone(); + data = bankdata[0]; + } + + /** + * Constructs an integer-based DataBuffer with the specified arrays, size, + * and offsets. + * The number of banks is equal to dataArray.length. Each array must + * be at least as large as size + the corresponding offset. There must + * be an entry in the offset array for each dataArray entry. For each + * bank, only elements offset through + * offset + size - 1 should be + * used by accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The integer arrays for the DataBuffer. + * @param size The size of the banks in the DataBuffer. + * @param offsets The offsets into each array. + */ + public DataBufferInt(int dataArray[][], int size, int offsets[]) { + super(UNTRACKABLE, TYPE_INT, size, dataArray.length, offsets); + bankdata = (int [][]) dataArray.clone(); + data = bankdata[0]; + } + + /** + * Returns the default (first) int data array in DataBuffer. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @return The first integer data array. + */ + public int[] getData() { + theTrackable.setUntrackable(); + return data; + } + + /** + * Returns the data array for the specified bank. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param bank The bank whose data array you want to get. + * @return The data array for the specified bank. + */ + public int[] getData(int bank) { + theTrackable.setUntrackable(); + return bankdata[bank]; + } + + /** + * Returns the data arrays for all banks. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @return All of the data arrays. + */ + public int[][] getBankData() { + theTrackable.setUntrackable(); + return (int [][]) bankdata.clone(); + } + + /** + * 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[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][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[i+offset] = val; + theTrackable.markDirty(); + } + + /** + * Sets the requested data array element in the specified bank + * to the integer value i. + * @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][i+offsets[bank]] = (int)val; + theTrackable.markDirty(); + } +} diff --git a/jdk/src/share/classes/java/awt/image/DataBufferShort.java b/jdk/src/share/classes/java/awt/image/DataBufferShort.java new file mode 100644 index 00000000000..bf7afe9a291 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/DataBufferShort.java @@ -0,0 +1,283 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +import static sun.java2d.StateTrackable.State.*; + +/** + * This class extends DataBuffer and stores data internally as shorts. + *

+ * + * Note that some implementations may function more efficiently + * if they can maintain control over how the data for an image is + * stored. + * For example, optimizations such as caching an image in video + * memory require that the implementation track all modifications + * to that data. + * Other implementations may operate better if they can store the + * data in locations other than a Java array. + * To maintain optimum compatibility with various optimizations + * it is best to avoid constructors and methods which expose the + * underlying storage as a Java array as noted below in the + * documentation for those methods. + * + */ +public final class DataBufferShort extends DataBuffer +{ + /** The default data bank. */ + short data[]; + + /** All data banks */ + short bankdata[][]; + + /** + * Constructs a short-based DataBuffer with a single bank and the + * specified size. + * + * @param size The size of the DataBuffer. + */ + public DataBufferShort(int size) { + super(STABLE, TYPE_SHORT,size); + data = new short[size]; + bankdata = new short[1][]; + bankdata[0] = data; + } + + /** + * Constructs a short-based DataBuffer with the specified number of + * banks all of which are the specified size. + * + * @param size The size of the banks in the DataBuffer. + * @param numBanks The number of banks in the aDataBuffer. + */ + public DataBufferShort(int size, int numBanks) { + super(STABLE, TYPE_SHORT,size,numBanks); + bankdata = new short[numBanks][]; + for (int i= 0; i < numBanks; i++) { + bankdata[i] = new short[size]; + } + data = bankdata[0]; + } + + /** + * Constructs a short-based DataBuffer with a single bank using the + * specified array. + * Only the first size elements should be used by accessors of + * this DataBuffer. dataArray must be large enough to + * hold size elements. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The short array for the DataBuffer. + * @param size The size of the DataBuffer bank. + */ + public DataBufferShort(short dataArray[], int size) { + super(UNTRACKABLE, TYPE_SHORT, size); + data = dataArray; + bankdata = new short[1][]; + bankdata[0] = data; + } + + /** + * Constructs a short-based DataBuffer with a single bank using the + * specified array, size, and offset. dataArray must have at least + * offset + size elements. Only elements offset + * through offset + size - 1 + * should be used by accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The short array for the DataBuffer. + * @param size The size of the DataBuffer bank. + * @param offset The offset into the dataArray. + */ + public DataBufferShort(short dataArray[], int size, int offset) { + super(UNTRACKABLE, TYPE_SHORT, size, 1, offset); + data = dataArray; + bankdata = new short[1][]; + bankdata[0] = data; + } + + /** + * Constructs a short-based DataBuffer with the specified arrays. + * The number of banks will be equal to dataArray.length. + * Only the first size elements of each array should be used by + * accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The short arrays for the DataBuffer. + * @param size The size of the banks in the DataBuffer. + */ + public DataBufferShort(short dataArray[][], int size) { + super(UNTRACKABLE, TYPE_SHORT, size, dataArray.length); + bankdata = (short[][]) dataArray.clone(); + data = bankdata[0]; + } + + /** + * Constructs a short-based DataBuffer with the specified arrays, size, + * and offsets. + * The number of banks is equal to dataArray.length. Each array must + * be at least as large as size + the corresponding offset. There must + * be an entry in the offset array for each dataArray entry. For each + * bank, only elements offset through + * offset + size - 1 should be + * used by accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The short arrays for the DataBuffer. + * @param size The size of the banks in the DataBuffer. + * @param offsets The offsets into each array. + */ + public DataBufferShort(short dataArray[][], int size, int offsets[]) { + super(UNTRACKABLE, TYPE_SHORT, size, dataArray.length, offsets); + bankdata = (short[][]) dataArray.clone(); + data = bankdata[0]; + } + + /** + * Returns the default (first) byte data array. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @return The first short data array. + */ + public short[] getData() { + theTrackable.setUntrackable(); + return data; + } + + /** + * Returns the data array for the specified bank. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param bank The bank whose data array you want to get. + * @return The data array for the specified bank. + */ + public short[] getData(int bank) { + theTrackable.setUntrackable(); + return bankdata[bank]; + } + + /** + * Returns the data arrays for all banks. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @return All of the data arrays. + */ + public short[][] getBankData() { + theTrackable.setUntrackable(); + return (short[][]) bankdata.clone(); + } + + /** + * 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 (int)(data[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 (int)(bankdata[bank][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[i+offset] = (short)val; + theTrackable.markDirty(); + } + + /** + * Sets the requested data array element in the specified bank + * from the given integer. + * @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][i+offsets[bank]] = (short)val; + theTrackable.markDirty(); + } +} diff --git a/jdk/src/share/classes/java/awt/image/DataBufferUShort.java b/jdk/src/share/classes/java/awt/image/DataBufferUShort.java new file mode 100644 index 00000000000..e059982e84e --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/DataBufferUShort.java @@ -0,0 +1,318 @@ +/* + * Portions Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +import static sun.java2d.StateTrackable.State.*; + +/** + * This class extends DataBuffer and stores data internally as + * shorts. Values stored in the short array(s) of this DataBuffer + * are treated as unsigned values. + *

+ * + * Note that some implementations may function more efficiently + * if they can maintain control over how the data for an image is + * stored. + * For example, optimizations such as caching an image in video + * memory require that the implementation track all modifications + * to that data. + * Other implementations may operate better if they can store the + * data in locations other than a Java array. + * To maintain optimum compatibility with various optimizations + * it is best to avoid constructors and methods which expose the + * underlying storage as a Java array as noted below in the + * documentation for those methods. + * + */ +public final class DataBufferUShort extends DataBuffer +{ + /** The default data bank. */ + short data[]; + + /** All data banks */ + short bankdata[][]; + + /** + * Constructs an unsigned-short based DataBuffer with a single bank and the + * specified size. + * + * @param size The size of the DataBuffer. + */ + public DataBufferUShort(int size) { + super(STABLE, TYPE_USHORT, size); + data = new short[size]; + bankdata = new short[1][]; + bankdata[0] = data; + } + + /** + * Constructs an unsigned-short based DataBuffer with the specified number of + * banks, all of which are the specified size. + * + * @param size The size of the banks in the DataBuffer. + * @param numBanks The number of banks in the aDataBuffer. + */ + public DataBufferUShort(int size, int numBanks) { + super(STABLE, TYPE_USHORT, size, numBanks); + bankdata = new short[numBanks][]; + for (int i= 0; i < numBanks; i++) { + bankdata[i] = new short[size]; + } + data = bankdata[0]; + } + + /** + * Constructs an unsigned-short based DataBuffer with a single bank + * using the specified array. + * Only the first size elements should be used by accessors of + * this DataBuffer. dataArray must be large enough to + * hold size elements. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The unsigned-short array for the DataBuffer. + * @param size The size of the DataBuffer bank. + */ + public DataBufferUShort(short dataArray[], int size) { + super(UNTRACKABLE, TYPE_USHORT, size); + if (dataArray == null) { + throw new NullPointerException("dataArray is null"); + } + data = dataArray; + bankdata = new short[1][]; + bankdata[0] = data; + } + + /** + * Constructs an unsigned-short based DataBuffer with a single bank + * using the specified array, size, and offset. dataArray must have at + * least offset + size elements. Only elements + * offset through offset + size - 1 should + * be used by accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The unsigned-short array for the DataBuffer. + * @param size The size of the DataBuffer bank. + * @param offset The offset into the dataArray. + */ + public DataBufferUShort(short dataArray[], int size, int offset) { + super(UNTRACKABLE, TYPE_USHORT, size, 1, offset); + if (dataArray == null) { + throw new NullPointerException("dataArray is null"); + } + if ((size+offset) > dataArray.length) { + throw new IllegalArgumentException("Length of dataArray is less "+ + " than size+offset."); + } + data = dataArray; + bankdata = new short[1][]; + bankdata[0] = data; + } + + /** + * Constructs an unsigned-short based DataBuffer with the specified arrays. + * The number of banks will be equal to dataArray.length. + * Only the first size elements of each array should be used by + * accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The unsigned-short arrays for the DataBuffer. + * @param size The size of the banks in the DataBuffer. + */ + public DataBufferUShort(short dataArray[][], int size) { + super(UNTRACKABLE, TYPE_USHORT, size, dataArray.length); + if (dataArray == null) { + throw new NullPointerException("dataArray is null"); + } + for (int i=0; i < dataArray.length; i++) { + if (dataArray[i] == null) { + throw new NullPointerException("dataArray["+i+"] is null"); + } + } + + bankdata = (short[][]) dataArray.clone(); + data = bankdata[0]; + } + + /** + * Constructs an unsigned-short based DataBuffer with specified arrays, + * size, and offsets. + * The number of banks is equal to dataArray.length. Each array must + * be at least as large as size + the corresponding offset. There must + * be an entry in the offset array for each dataArray entry. For each + * bank, only elements offset through + * offset + size - 1 should be + * used by accessors of this DataBuffer. + *

+ * Note that {@code DataBuffer} objects created by this constructor + * may be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param dataArray The unsigned-short arrays for the DataBuffer. + * @param size The size of the banks in the DataBuffer. + * @param offsets The offsets into each array. + */ + public DataBufferUShort(short dataArray[][], int size, int offsets[]) { + super(UNTRACKABLE, TYPE_USHORT, size, dataArray.length, offsets); + if (dataArray == null) { + throw new NullPointerException("dataArray is null"); + } + for (int i=0; i < dataArray.length; i++) { + if (dataArray[i] == null) { + throw new NullPointerException("dataArray["+i+"] is null"); + } + if ((size+offsets[i]) > dataArray[i].length) { + throw new IllegalArgumentException("Length of dataArray["+i+ + "] is less than size+"+ + "offsets["+i+"]."); + } + + } + bankdata = (short[][]) dataArray.clone(); + data = bankdata[0]; + } + + /** + * Returns the default (first) unsigned-short data array. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @return The first unsigned-short data array. + */ + public short[] getData() { + theTrackable.setUntrackable(); + return data; + } + + /** + * Returns the data array for the specified bank. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @param bank The bank whose data array you want to get. + * @return The data array for the specified bank. + */ + public short[] getData(int bank) { + theTrackable.setUntrackable(); + return bankdata[bank]; + } + + /** + * Returns the data arrays for all banks. + *

+ * Note that calling this method may cause this {@code DataBuffer} + * object to be incompatible with performance + * optimizations used by some implementations (such as caching + * an associated image in video memory). + * + * @return All of the data arrays. + */ + public short[][] getBankData() { + theTrackable.setUntrackable(); + return (short[][]) bankdata.clone(); + } + + /** + * 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 (int)(data[i+offset]&0xffff); + } + + /** + * 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 (int)(bankdata[bank][i+offsets[bank]]&0xffff); + } + + /** + * 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[i+offset] = (short)(val&0xffff); + theTrackable.markDirty(); + } + + /** + * Sets the requested data array element in the specified bank + * from the given integer. + * @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][i+offsets[bank]] = (short)(val&0xffff); + theTrackable.markDirty(); + } +} diff --git a/jdk/src/share/classes/java/awt/image/MultiPixelPackedSampleModel.java b/jdk/src/share/classes/java/awt/image/MultiPixelPackedSampleModel.java new file mode 100644 index 00000000000..1b8f9bbd76b --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/MultiPixelPackedSampleModel.java @@ -0,0 +1,699 @@ +/* + * Portions Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +/** + * The MultiPixelPackedSampleModel class represents + * one-banded images and can pack multiple one-sample + * pixels into one data element. Pixels are not allowed to span data elements. + * The data type can be DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, + * or DataBuffer.TYPE_INT. Each pixel must be a power of 2 number of bits + * and a power of 2 number of pixels must fit exactly in one data element. + * Pixel bit stride is equal to the number of bits per pixel. Scanline + * stride is in data elements and the last several data elements might be + * padded with unused pixels. Data bit offset is the offset in bits from + * the beginning of the {@link DataBuffer} to the first pixel and must be + * a multiple of pixel bit stride. + *

+ * The following code illustrates extracting the bits for pixel + * x, y from DataBuffer data + * and storing the pixel data in data elements of type + * dataType: + *

+ *      int dataElementSize = DataBuffer.getDataTypeSize(dataType);
+ *      int bitnum = dataBitOffset + x*pixelBitStride;
+ *      int element = data.getElem(y*scanlineStride + bitnum/dataElementSize);
+ *      int shift = dataElementSize - (bitnum & (dataElementSize-1))
+ *                  - pixelBitStride;
+ *      int pixel = (element >> shift) & ((1 << pixelBitStride) - 1);
+ * 
+ */ + +public class MultiPixelPackedSampleModel extends SampleModel +{ + /** The number of bits from one pixel to the next. */ + int pixelBitStride; + + /** Bitmask that extracts the rightmost pixel of a data element. */ + int bitMask; + + /** + * The number of pixels that fit in a data element. Also used + * as the number of bits per pixel. + */ + int pixelsPerDataElement; + + /** The size of a data element in bits. */ + int dataElementSize; + + /** The bit offset into the data array where the first pixel begins. + */ + int dataBitOffset; + + /** ScanlineStride of the data buffer described in data array elements. */ + int scanlineStride; + + /** + * Constructs a MultiPixelPackedSampleModel with the + * specified data type, width, height and number of bits per pixel. + * @param dataType the data type for storing samples + * @param w the width, in pixels, of the region of + * image data described + * @param h the height, in pixels, of the region of + * image data described + * @param numberOfBits the number of bits per pixel + * @throws IllegalArgumentException if dataType is not + * either DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, or + * DataBuffer.TYPE_INT + */ + public MultiPixelPackedSampleModel(int dataType, + int w, + int h, + int numberOfBits) { + this(dataType,w,h, + numberOfBits, + (w*numberOfBits+DataBuffer.getDataTypeSize(dataType)-1)/ + DataBuffer.getDataTypeSize(dataType), + 0); + if (dataType != DataBuffer.TYPE_BYTE && + dataType != DataBuffer.TYPE_USHORT && + dataType != DataBuffer.TYPE_INT) { + throw new IllegalArgumentException("Unsupported data type "+ + dataType); + } + } + + /** + * Constructs a MultiPixelPackedSampleModel with + * specified data type, width, height, number of bits per pixel, + * scanline stride and data bit offset. + * @param dataType the data type for storing samples + * @param w the width, in pixels, of the region of + * image data described + * @param h the height, in pixels, of the region of + * image data described + * @param numberOfBits the number of bits per pixel + * @param scanlineStride the line stride of the image data + * @param dataBitOffset the data bit offset for the region of image + * data described + * @exception RasterFormatException if the number of bits per pixel + * is not a power of 2 or if a power of 2 number of + * pixels do not fit in one data element. + * @throws IllegalArgumentException if w or + * h is not greater than 0 + * @throws IllegalArgumentException if dataType is not + * either DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, or + * DataBuffer.TYPE_INT + */ + public MultiPixelPackedSampleModel(int dataType, int w, int h, + int numberOfBits, + int scanlineStride, + int dataBitOffset) { + super(dataType, w, h, 1); + if (dataType != DataBuffer.TYPE_BYTE && + dataType != DataBuffer.TYPE_USHORT && + dataType != DataBuffer.TYPE_INT) { + throw new IllegalArgumentException("Unsupported data type "+ + dataType); + } + this.dataType = dataType; + this.pixelBitStride = numberOfBits; + this.scanlineStride = scanlineStride; + this.dataBitOffset = dataBitOffset; + this.dataElementSize = DataBuffer.getDataTypeSize(dataType); + this.pixelsPerDataElement = dataElementSize/numberOfBits; + if (pixelsPerDataElement*numberOfBits != dataElementSize) { + throw new RasterFormatException("MultiPixelPackedSampleModel " + + "does not allow pixels to " + + "span data element boundaries"); + } + this.bitMask = (1 << numberOfBits) - 1; + } + + + /** + * Creates a new MultiPixelPackedSampleModel with the + * specified width and height. The new + * MultiPixelPackedSampleModel has the + * same storage data type and number of bits per pixel as this + * MultiPixelPackedSampleModel. + * @param w the specified width + * @param h the specified height + * @return a {@link SampleModel} with the specified width and height + * and with the same storage data type and number of bits per pixel + * as this MultiPixelPackedSampleModel. + * @throws IllegalArgumentException if w or + * h is not greater than 0 + */ + public SampleModel createCompatibleSampleModel(int w, int h) { + SampleModel sampleModel = + new MultiPixelPackedSampleModel(dataType, w, h, pixelBitStride); + return sampleModel; + } + + /** + * Creates a DataBuffer that corresponds to this + * MultiPixelPackedSampleModel. The + * DataBuffer object's data type and size + * is consistent with this MultiPixelPackedSampleModel. + * The DataBuffer has a single bank. + * @return a DataBuffer with the same data type and + * size as this MultiPixelPackedSampleModel. + */ + public DataBuffer createDataBuffer() { + DataBuffer dataBuffer = null; + + int size = (int)scanlineStride*height; + switch (dataType) { + case DataBuffer.TYPE_BYTE: + dataBuffer = new DataBufferByte(size+(dataBitOffset+7)/8); + break; + case DataBuffer.TYPE_USHORT: + dataBuffer = new DataBufferUShort(size+(dataBitOffset+15)/16); + break; + case DataBuffer.TYPE_INT: + dataBuffer = new DataBufferInt(size+(dataBitOffset+31)/32); + break; + } + return dataBuffer; + } + + /** + * Returns the number of data elements needed to transfer one pixel + * via the {@link #getDataElements} and {@link #setDataElements} + * methods. For a MultiPixelPackedSampleModel, this is + * one. + * @return the number of data elements. + */ + public int getNumDataElements() { + return 1; + } + + /** + * Returns the number of bits per sample for all bands. + * @return the number of bits per sample. + */ + public int[] getSampleSize() { + int sampleSize[] = {pixelBitStride}; + return sampleSize; + } + + /** + * Returns the number of bits per sample for the specified band. + * @param band the specified band + * @return the number of bits per sample for the specified band. + */ + public int getSampleSize(int band) { + return pixelBitStride; + } + + /** + * Returns the offset of pixel (x, y) in data array elements. + * @param x the X coordinate of the specified pixel + * @param y the Y coordinate of the specified pixel + * @return the offset of the specified pixel. + */ + public int getOffset(int x, int y) { + int offset = y * scanlineStride; + offset += (x*pixelBitStride+dataBitOffset)/dataElementSize; + return offset; + } + + /** + * Returns the offset, in bits, into the data element in which it is + * stored for the xth pixel of a scanline. + * This offset is the same for all scanlines. + * @param x the specified pixel + * @return the bit offset of the specified pixel. + */ + public int getBitOffset(int x){ + return (x*pixelBitStride+dataBitOffset)%dataElementSize; + } + + /** + * Returns the scanline stride. + * @return the scanline stride of this + * MultiPixelPackedSampleModel. + */ + public int getScanlineStride() { + return scanlineStride; + } + + /** + * Returns the pixel bit stride in bits. This value is the same as + * the number of bits per pixel. + * @return the pixelBitStride of this + * MultiPixelPackedSampleModel. + */ + public int getPixelBitStride() { + return pixelBitStride; + } + + /** + * Returns the data bit offset in bits. + * @return the dataBitOffset of this + * MultiPixelPackedSampleModel. + */ + public int getDataBitOffset() { + return dataBitOffset; + } + + /** + * Returns the TransferType used to transfer pixels by way of the + * getDataElements and setDataElements + * methods. The TransferType might or might not be the same as the + * storage DataType. The TransferType is one of + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, + * or DataBuffer.TYPE_INT. + * @return the transfertype. + */ + public int getTransferType() { + if (pixelBitStride > 16) + return DataBuffer.TYPE_INT; + else if (pixelBitStride > 8) + return DataBuffer.TYPE_USHORT; + else + return DataBuffer.TYPE_BYTE; + } + + /** + * Creates a new MultiPixelPackedSampleModel with a + * subset of the bands of this + * MultiPixelPackedSampleModel. Since a + * MultiPixelPackedSampleModel only has one band, the + * bands argument must have a length of one and indicate the zeroth + * band. + * @param bands the specified bands + * @return a new SampleModel with a subset of bands of + * this MultiPixelPackedSampleModel. + * @exception RasterFormatException if the number of bands requested + * is not one. + * @throws IllegalArgumentException if w or + * h is not greater than 0 + */ + public SampleModel createSubsetSampleModel(int bands[]) { + if (bands != null) { + if (bands.length != 1) + throw new RasterFormatException("MultiPixelPackedSampleModel has " + + "only one band."); + } + SampleModel sm = createCompatibleSampleModel(width, height); + return sm; + } + + /** + * Returns as int the sample in a specified band for the + * pixel located at (x, y). An + * ArrayIndexOutOfBoundsException is thrown if the + * coordinates are not in bounds. + * @param x the X coordinate of the specified pixel + * @param y the Y coordinate of the specified pixel + * @param b the band to return, which is assumed to be 0 + * @param data the DataBuffer containing the image + * data + * @return the specified band containing the sample of the specified + * pixel. + * @exception ArrayIndexOutOfBoundException if the specified + * coordinates are not in bounds. + * @see #setSample(int, int, int, int, DataBuffer) + */ + public int getSample(int x, int y, int b, DataBuffer data) { + // 'b' must be 0 + if ((x < 0) || (y < 0) || (x >= width) || (y >= height) || + (b != 0)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int bitnum = dataBitOffset + x*pixelBitStride; + int element = data.getElem(y*scanlineStride + bitnum/dataElementSize); + int shift = dataElementSize - (bitnum & (dataElementSize-1)) + - pixelBitStride; + return (element >> shift) & bitMask; + } + + /** + * Sets a sample in the specified band for the pixel located at + * (x, y) in the DataBuffer using an + * int for input. + * An ArrayIndexOutOfBoundsException is thrown if the + * coordinates are not in bounds. + * @param x the X coordinate of the specified pixel + * @param y the Y coordinate of the specified pixel + * @param b the band to return, which is assumed to be 0 + * @param s the input sample as an int + * @param data the DataBuffer where image data is stored + * @exception ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds. + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, int s, + DataBuffer data) { + // 'b' must be 0 + if ((x < 0) || (y < 0) || (x >= width) || (y >= height) || + (b != 0)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int bitnum = dataBitOffset + x * pixelBitStride; + int index = y * scanlineStride + (bitnum / dataElementSize); + int shift = dataElementSize - (bitnum & (dataElementSize-1)) + - pixelBitStride; + int element = data.getElem(index); + element &= ~(bitMask << shift); + element |= (s & bitMask) << shift; + data.setElem(index,element); + } + + /** + * Returns data for a single pixel in a primitive array of type + * TransferType. For a MultiPixelPackedSampleModel, + * the array has one element, and the type is the smallest of + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT + * that can hold a single pixel. Generally, obj + * should be passed in as null, so that the + * Object is created automatically and is the + * correct primitive data type. + *

+ * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is + * described by MultiPixelPackedSampleModel + * mppsm1, to DataBuffer db2, + * whose storage layout is described by + * MultiPixelPackedSampleModel mppsm2. + * The transfer is generally more efficient than using + * getPixel or setPixel. + *

+     *       MultiPixelPackedSampleModel mppsm1, mppsm2;
+     *       DataBufferInt db1, db2;
+     *       mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null,
+     *                              db1), db2);
+     * 
+ * Using getDataElements or setDataElements + * to transfer between two DataBuffer/SampleModel pairs + * is legitimate if the SampleModels have the same number + * of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * If obj is not null, it should be a + * primitive array of type TransferType. Otherwise, a + * ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException is thrown if the + * coordinates are not in bounds, or if obj is not + * null and is not large enough to hold the pixel data. + * @param x the X coordinate of the specified pixel + * @param y the Y coordinate of the specified pixel + * @param obj a primitive array in which to return the pixel data or + * null. + * @param data the DataBuffer containing the image data. + * @return an Object containing data for the specified + * pixel. + * @exception ClassCastException if obj is not a + * primitive array of type TransferType or is not null + * @exception ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if obj is not null or + * not large enough to hold the pixel data + * @see #setDataElements(int, int, Object, DataBuffer) + */ + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int type = getTransferType(); + int bitnum = dataBitOffset + x*pixelBitStride; + int shift = dataElementSize - (bitnum & (dataElementSize-1)) + - pixelBitStride; + int element = 0; + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] bdata; + + if (obj == null) + bdata = new byte[1]; + else + bdata = (byte[])obj; + + element = data.getElem(y*scanlineStride + + bitnum/dataElementSize); + bdata[0] = (byte)((element >> shift) & bitMask); + + obj = (Object)bdata; + break; + + case DataBuffer.TYPE_USHORT: + + short[] sdata; + + if (obj == null) + sdata = new short[1]; + else + sdata = (short[])obj; + + element = data.getElem(y*scanlineStride + + bitnum/dataElementSize); + sdata[0] = (short)((element >> shift) & bitMask); + + obj = (Object)sdata; + break; + + case DataBuffer.TYPE_INT: + + int[] idata; + + if (obj == null) + idata = new int[1]; + else + idata = (int[])obj; + + element = data.getElem(y*scanlineStride + + bitnum/dataElementSize); + idata[0] = (element >> shift) & bitMask; + + obj = (Object)idata; + break; + } + + return obj; + } + + /** + * Returns the specified single band pixel in the first element + * of an int array. + * ArrayIndexOutOfBoundsException is thrown if the + * coordinates are not in bounds. + * @param x the X coordinate of the specified pixel + * @param y the Y coordinate of the specified pixel + * @param iArray the array containing the pixel to be returned or + * null + * @param data the DataBuffer where image data is stored + * @return an array containing the specified pixel. + * @exception ArrayIndexOutOfBoundsException if the coordinates + * are not in bounds + * @see #setPixel(int, int, int[], DataBuffer) + */ + public int[] getPixel(int x, int y, int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int pixels[]; + if (iArray != null) { + pixels = iArray; + } else { + pixels = new int [numBands]; + } + int bitnum = dataBitOffset + x*pixelBitStride; + int element = data.getElem(y*scanlineStride + bitnum/dataElementSize); + int shift = dataElementSize - (bitnum & (dataElementSize-1)) + - pixelBitStride; + pixels[0] = (element >> shift) & bitMask; + return pixels; + } + + /** + * Sets the data for a single pixel in the specified + * DataBuffer from a primitive array of type + * TransferType. For a MultiPixelPackedSampleModel, + * only the first element of the array holds valid data, + * and the type must be the smallest of + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT + * that can hold a single pixel. + *

+ * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is + * described by MultiPixelPackedSampleModel + * mppsm1, to DataBuffer db2, + * whose storage layout is described by + * MultiPixelPackedSampleModel mppsm2. + * The transfer is generally more efficient than using + * getPixel or setPixel. + *

+     *       MultiPixelPackedSampleModel mppsm1, mppsm2;
+     *       DataBufferInt db1, db2;
+     *       mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null,
+     *                              db1), db2);
+     * 
+ * Using getDataElements or setDataElements to + * transfer between two DataBuffer/SampleModel pairs is + * legitimate if the SampleModel objects have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * obj must be a primitive array of type TransferType. + * Otherwise, a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException is thrown if the + * coordinates are not in bounds, or if obj is not large + * enough to hold the pixel data. + * @param x the X coordinate of the pixel location + * @param y the Y coordinate of the pixel location + * @param obj a primitive array containing pixel data + * @param data the DataBuffer containing the image data + * @see #getDataElements(int, int, Object, DataBuffer) + */ + public void setDataElements(int x, int y, Object obj, DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int type = getTransferType(); + int bitnum = dataBitOffset + x * pixelBitStride; + int index = y * scanlineStride + (bitnum / dataElementSize); + int shift = dataElementSize - (bitnum & (dataElementSize-1)) + - pixelBitStride; + int element = data.getElem(index); + element &= ~(bitMask << shift); + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] barray = (byte[])obj; + element |= ( ((int)(barray[0])&0xff) & bitMask) << shift; + data.setElem(index, element); + break; + + case DataBuffer.TYPE_USHORT: + + short[] sarray = (short[])obj; + element |= ( ((int)(sarray[0])&0xffff) & bitMask) << shift; + data.setElem(index, element); + break; + + case DataBuffer.TYPE_INT: + + int[] iarray = (int[])obj; + element |= (iarray[0] & bitMask) << shift; + data.setElem(index, element); + break; + } + } + + /** + * Sets a pixel in the DataBuffer using an + * int array for input. + * ArrayIndexOutOfBoundsException is thrown if + * the coordinates are not in bounds. + * @param x the X coordinate of the pixel location + * @param y the Y coordinate of the pixel location + * @param iArray the input pixel in an int array + * @param data the DataBuffer containing the image data + * @see #getPixel(int, int, int[], DataBuffer) + */ + public void setPixel(int x, int y, int[] iArray, DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int bitnum = dataBitOffset + x * pixelBitStride; + int index = y * scanlineStride + (bitnum / dataElementSize); + int shift = dataElementSize - (bitnum & (dataElementSize-1)) + - pixelBitStride; + int element = data.getElem(index); + element &= ~(bitMask << shift); + element |= (iArray[0] & bitMask) << shift; + data.setElem(index,element); + } + + public boolean equals(Object o) { + if ((o == null) || !(o instanceof MultiPixelPackedSampleModel)) { + return false; + } + + MultiPixelPackedSampleModel that = (MultiPixelPackedSampleModel)o; + return this.width == that.width && + this.height == that.height && + this.numBands == that.numBands && + this.dataType == that.dataType && + this.pixelBitStride == that.pixelBitStride && + this.bitMask == that.bitMask && + this.pixelsPerDataElement == that.pixelsPerDataElement && + this.dataElementSize == that.dataElementSize && + this.dataBitOffset == that.dataBitOffset && + this.scanlineStride == that.scanlineStride; + } + + // If we implement equals() we must also implement hashCode + public int hashCode() { + int hash = 0; + hash = width; + hash <<= 8; + hash ^= height; + hash <<= 8; + hash ^= numBands; + hash <<= 8; + hash ^= dataType; + hash <<= 8; + hash ^= pixelBitStride; + hash <<= 8; + hash ^= bitMask; + hash <<= 8; + hash ^= pixelsPerDataElement; + hash <<= 8; + hash ^= dataElementSize; + hash <<= 8; + hash ^= dataBitOffset; + hash <<= 8; + hash ^= scanlineStride; + return hash; + } +} diff --git a/jdk/src/share/classes/java/awt/image/Raster.java b/jdk/src/share/classes/java/awt/image/Raster.java new file mode 100644 index 00000000000..39082c39a58 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/Raster.java @@ -0,0 +1,1777 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + + +package java.awt.image; +import java.awt.Rectangle; +import java.awt.Point; + +import sun.awt.image.ByteInterleavedRaster; +import sun.awt.image.ShortInterleavedRaster; +import sun.awt.image.IntegerInterleavedRaster; +import sun.awt.image.ByteBandedRaster; +import sun.awt.image.ShortBandedRaster; +import sun.awt.image.BytePackedRaster; +import sun.awt.image.SunWritableRaster; + +/** + * A class representing a rectangular array of pixels. A Raster + * encapsulates a DataBuffer that stores the sample values and a + * SampleModel that describes how to locate a given sample value in a + * DataBuffer. + *

+ * A Raster defines values for pixels occupying a particular + * rectangular area of the plane, not necessarily including (0, 0). + * The rectangle, known as the Raster's bounding rectangle and + * available by means of the getBounds method, is defined by minX, + * minY, width, and height values. The minX and minY values define + * the coordinate of the upper left corner of the Raster. References + * to pixels outside of the bounding rectangle may result in an + * exception being thrown, or may result in references to unintended + * elements of the Raster's associated DataBuffer. It is the user's + * responsibility to avoid accessing such pixels. + *

+ * A SampleModel describes how samples of a Raster + * are stored in the primitive array elements of a DataBuffer. + * Samples may be stored one per data element, as in a + * PixelInterleavedSampleModel or BandedSampleModel, or packed several to + * an element, as in a SinglePixelPackedSampleModel or + * MultiPixelPackedSampleModel. The SampleModel is also + * controls whether samples are sign extended, allowing unsigned + * data to be stored in signed Java data types such as byte, short, and + * int. + *

+ * Although a Raster may live anywhere in the plane, a SampleModel + * makes use of a simple coordinate system that starts at (0, 0). A + * Raster therefore contains a translation factor that allows pixel + * locations to be mapped between the Raster's coordinate system and + * that of the SampleModel. The translation from the SampleModel + * coordinate system to that of the Raster may be obtained by the + * getSampleModelTranslateX and getSampleModelTranslateY methods. + *

+ * A Raster may share a DataBuffer with another Raster either by + * explicit construction or by the use of the createChild and + * createTranslatedChild methods. Rasters created by these methods + * can return a reference to the Raster they were created from by + * means of the getParent method. For a Raster that was not + * constructed by means of a call to createTranslatedChild or + * createChild, getParent will return null. + *

+ * The createTranslatedChild method returns a new Raster that + * shares all of the data of the current Raster, but occupies a + * bounding rectangle of the same width and height but with a + * different starting point. For example, if the parent Raster + * occupied the region (10, 10) to (100, 100), and the translated + * Raster was defined to start at (50, 50), then pixel (20, 20) of the + * parent and pixel (60, 60) of the child occupy the same location in + * the DataBuffer shared by the two Rasters. In the first case, (-10, + * -10) should be added to a pixel coordinate to obtain the + * corresponding SampleModel coordinate, and in the second case (-50, + * -50) should be added. + *

+ * The translation between a parent and child Raster may be + * determined by subtracting the child's sampleModelTranslateX and + * sampleModelTranslateY values from those of the parent. + *

+ * The createChild method may be used to create a new Raster + * occupying only a subset of its parent's bounding rectangle + * (with the same or a translated coordinate system) or + * with a subset of the bands of its parent. + *

+ * All constructors are protected. The correct way to create a + * Raster is to use one of the static create methods defined in this + * class. These methods create instances of Raster that use the + * standard Interleaved, Banded, and Packed SampleModels and that may + * be processed more efficiently than a Raster created by combining + * an externally generated SampleModel and DataBuffer. + * @see java.awt.image.DataBuffer + * @see java.awt.image.SampleModel + * @see java.awt.image.PixelInterleavedSampleModel + * @see java.awt.image.BandedSampleModel + * @see java.awt.image.SinglePixelPackedSampleModel + * @see java.awt.image.MultiPixelPackedSampleModel + */ +public class Raster { + + /** + * The SampleModel that describes how pixels from this Raster + * are stored in the DataBuffer. + */ + protected SampleModel sampleModel; + + /** The DataBuffer that stores the image data. */ + protected DataBuffer dataBuffer; + + /** The X coordinate of the upper-left pixel of this Raster. */ + protected int minX; + + /** The Y coordinate of the upper-left pixel of this Raster. */ + protected int minY; + + /** The width of this Raster. */ + protected int width; + + /** The height of this Raster. */ + protected int height; + + /** + * The X translation from the coordinate space of the + * Raster's SampleModel to that of the Raster. + */ + protected int sampleModelTranslateX; + + /** + * The Y translation from the coordinate space of the + * Raster's SampleModel to that of the Raster. + */ + protected int sampleModelTranslateY; + + /** The number of bands in the Raster. */ + protected int numBands; + + /** The number of DataBuffer data elements per pixel. */ + protected int numDataElements; + + /** The parent of this Raster, or null. */ + protected Raster parent; + + static private native void initIDs(); + static { + ColorModel.loadLibraries(); + initIDs(); + } + + /** + * Creates a Raster based on a PixelInterleavedSampleModel with the + * specified data type, width, height, and number of bands. + * + *

The upper left corner of the Raster is given by the + * location argument. If location is null, (0, 0) will be used. + * The dataType parameter should be one of the enumerated values + * defined in the DataBuffer class. + * + *

Note that interleaved DataBuffer.TYPE_INT + * Rasters are not supported. To create a 1-band Raster of type + * DataBuffer.TYPE_INT, use + * Raster.createPackedRaster(). + *

The only dataTypes supported currently are TYPE_BYTE + * and TYPE_USHORT. + * @param dataType the data type for storing samples + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param bands the number of bands + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified data type, + * width, height and number of bands. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + */ + public static WritableRaster createInterleavedRaster(int dataType, + int w, int h, + int bands, + Point location) { + int[] bandOffsets = new int[bands]; + for (int i = 0; i < bands; i++) { + bandOffsets[i] = i; + } + return createInterleavedRaster(dataType, w, h, w*bands, bands, + bandOffsets, location); + } + + /** + * Creates a Raster based on a PixelInterleavedSampleModel with the + * specified data type, width, height, scanline stride, pixel + * stride, and band offsets. The number of bands is inferred from + * bandOffsets.length. + * + *

The upper left corner of the Raster is given by the + * location argument. If location is null, (0, 0) will be used. + * The dataType parameter should be one of the enumerated values + * defined in the DataBuffer class. + * + *

Note that interleaved DataBuffer.TYPE_INT + * Rasters are not supported. To create a 1-band Raster of type + * DataBuffer.TYPE_INT, use + * Raster.createPackedRaster(). + *

The only dataTypes supported currently are TYPE_BYTE + * and TYPE_USHORT. + * @param dataType the data type for storing samples + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param scanlineStride the line stride of the image data + * @param pixelStride the pixel stride of the image data + * @param bandOffsets the offsets of all bands + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified data type, + * width, height, scanline stride, pixel stride and band + * offsets. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + * @throws IllegalArgumentException if dataType is not + * one of the supported data types, which are + * DataBuffer.TYPE_BYTE, or + * DataBuffer.TYPE_USHORT. + */ + public static WritableRaster createInterleavedRaster(int dataType, + int w, int h, + int scanlineStride, + int pixelStride, + int bandOffsets[], + Point location) { + DataBuffer d; + int bands = bandOffsets.length; + + int maxBandOff = bandOffsets[0]; + for (int i=1; i < bands; i++) { + if (bandOffsets[i] > maxBandOff) { + maxBandOff = bandOffsets[i]; + } + } + int size = maxBandOff + scanlineStride*(h-1) + pixelStride*(w-1) + 1; + switch(dataType) { + case DataBuffer.TYPE_BYTE: + d = new DataBufferByte(size); + break; + + case DataBuffer.TYPE_USHORT: + d = new DataBufferUShort(size); + break; + + default: + throw new IllegalArgumentException("Unsupported data type " + + dataType); + } + + return createInterleavedRaster(d, w, h, scanlineStride, + pixelStride, bandOffsets, location); + } + + /** + * Creates a Raster based on a BandedSampleModel with the + * specified data type, width, height, and number of bands. + * + *

The upper left corner of the Raster is given by the + * location argument. If location is null, (0, 0) will be used. + * The dataType parameter should be one of the enumerated values + * defined in the DataBuffer class. + * + *

The only dataTypes supported currently are TYPE_BYTE, TYPE_USHORT, + * and TYPE_INT. + * @param dataType the data type for storing samples + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param bands the number of bands + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified data type, + * width, height and number of bands. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + * @throws ArrayIndexOutOfBoundsException if bands + * is less than 1 + */ + public static WritableRaster createBandedRaster(int dataType, + int w, int h, + int bands, + Point location) { + if (bands < 1) { + throw new ArrayIndexOutOfBoundsException("Number of bands ("+ + bands+") must"+ + " be greater than 0"); + } + int[] bankIndices = new int[bands]; + int[] bandOffsets = new int[bands]; + for (int i = 0; i < bands; i++) { + bankIndices[i] = i; + bandOffsets[i] = 0; + } + + return createBandedRaster(dataType, w, h, w, + bankIndices, bandOffsets, + location); + } + + /** + * Creates a Raster based on a BandedSampleModel with the + * specified data type, width, height, scanline stride, bank + * indices and band offsets. The number of bands is inferred from + * bankIndices.length and bandOffsets.length, which must be the + * same. + * + *

The upper left corner of the Raster is given by the + * location argument. The dataType parameter should be one of the + * enumerated values defined in the DataBuffer class. + * + *

The only dataTypes supported currently are TYPE_BYTE, TYPE_USHORT, + * and TYPE_INT. + * @param dataType the data type for storing samples + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param scanlineStride the line stride of the image data + * @param bankIndices the bank indices for each band + * @param bandOffsets the offsets of all bands + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified data type, + * width, height, scanline stride, bank indices and band + * offsets. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + * @throws IllegalArgumentException if dataType is not + * one of the supported data types, which are + * DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT + * or DataBuffer.TYPE_INT + * @throws ArrayIndexOutOfBoundsException if bankIndices + * or bandOffsets is null + */ + public static WritableRaster createBandedRaster(int dataType, + int w, int h, + int scanlineStride, + int bankIndices[], + int bandOffsets[], + Point location) { + DataBuffer d; + int bands = bandOffsets.length; + + if (bankIndices == null) { + throw new + ArrayIndexOutOfBoundsException("Bank indices array is null"); + } + if (bandOffsets == null) { + throw new + ArrayIndexOutOfBoundsException("Band offsets array is null"); + } + + // Figure out the #banks and the largest band offset + int maxBank = bankIndices[0]; + int maxBandOff = bandOffsets[0]; + for (int i = 1; i < bands; i++) { + if (bankIndices[i] > maxBank) { + maxBank = bankIndices[i]; + } + if (bandOffsets[i] > maxBandOff) { + maxBandOff = bandOffsets[i]; + } + } + int banks = maxBank + 1; + int size = maxBandOff + scanlineStride*(h-1) + (w-1) + 1; + + switch(dataType) { + case DataBuffer.TYPE_BYTE: + d = new DataBufferByte(size, banks); + break; + + case DataBuffer.TYPE_USHORT: + d = new DataBufferUShort(size, banks); + break; + + case DataBuffer.TYPE_INT: + d = new DataBufferInt(size, banks); + break; + + default: + throw new IllegalArgumentException("Unsupported data type " + + dataType); + } + + return createBandedRaster(d, w, h, scanlineStride, + bankIndices, bandOffsets, location); + } + + /** + * Creates a Raster based on a SinglePixelPackedSampleModel with + * the specified data type, width, height, and band masks. + * The number of bands is inferred from bandMasks.length. + * + *

The upper left corner of the Raster is given by the + * location argument. If location is null, (0, 0) will be used. + * The dataType parameter should be one of the enumerated values + * defined in the DataBuffer class. + * + *

The only dataTypes supported currently are TYPE_BYTE, TYPE_USHORT, + * and TYPE_INT. + * @param dataType the data type for storing samples + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param bandMasks an array containing an entry for each band + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified data type, + * width, height, and band masks. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + * @throws IllegalArgumentException if dataType is not + * one of the supported data types, which are + * DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT + * or DataBuffer.TYPE_INT + */ + public static WritableRaster createPackedRaster(int dataType, + int w, int h, + int bandMasks[], + Point location) { + DataBuffer d; + + switch(dataType) { + case DataBuffer.TYPE_BYTE: + d = new DataBufferByte(w*h); + break; + + case DataBuffer.TYPE_USHORT: + d = new DataBufferUShort(w*h); + break; + + case DataBuffer.TYPE_INT: + d = new DataBufferInt(w*h); + break; + + default: + throw new IllegalArgumentException("Unsupported data type " + + dataType); + } + + return createPackedRaster(d, w, h, w, bandMasks, location); + } + + /** + * Creates a Raster based on a packed SampleModel with the + * specified data type, width, height, number of bands, and bits + * per band. If the number of bands is one, the SampleModel will + * be a MultiPixelPackedSampleModel. + * + *

If the number of bands is more than one, the SampleModel + * will be a SinglePixelPackedSampleModel, with each band having + * bitsPerBand bits. In either case, the requirements on dataType + * and bitsPerBand imposed by the corresponding SampleModel must + * be met. + * + *

The upper left corner of the Raster is given by the + * location argument. If location is null, (0, 0) will be used. + * The dataType parameter should be one of the enumerated values + * defined in the DataBuffer class. + * + *

The only dataTypes supported currently are TYPE_BYTE, TYPE_USHORT, + * and TYPE_INT. + * @param dataType the data type for storing samples + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param bands the number of bands + * @param bitsPerBand the number of bits per band + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified data type, + * width, height, number of bands, and bits per band. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + * @throws IllegalArgumentException if the product of + * bitsPerBand and bands is + * greater than the number of bits held by + * dataType + * @throws IllegalArgumentException if bitsPerBand or + * bands is not greater than zero + * @throws IllegalArgumentException if dataType is not + * one of the supported data types, which are + * DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT + * or DataBuffer.TYPE_INT + */ + public static WritableRaster createPackedRaster(int dataType, + int w, int h, + int bands, + int bitsPerBand, + Point location) { + DataBuffer d; + + if (bands <= 0) { + throw new IllegalArgumentException("Number of bands ("+bands+ + ") must be greater than 0"); + } + + if (bitsPerBand <= 0) { + throw new IllegalArgumentException("Bits per band ("+bitsPerBand+ + ") must be greater than 0"); + } + + if (bands != 1) { + int[] masks = new int[bands]; + int mask = (1 << bitsPerBand) - 1; + int shift = (bands-1)*bitsPerBand; + + /* Make sure the total mask size will fit in the data type */ + if (shift+bitsPerBand > DataBuffer.getDataTypeSize(dataType)) { + throw new IllegalArgumentException("bitsPerBand("+ + bitsPerBand+") * bands is "+ + " greater than data type "+ + "size."); + } + switch(dataType) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + break; + default: + throw new IllegalArgumentException("Unsupported data type " + + dataType); + } + + for (int i = 0; i < bands; i++) { + masks[i] = mask << shift; + shift = shift - bitsPerBand; + } + + return createPackedRaster(dataType, w, h, masks, location); + } + else { + double fw = w; + switch(dataType) { + case DataBuffer.TYPE_BYTE: + d = new DataBufferByte((int)(Math.ceil(fw/(8/bitsPerBand)))*h); + break; + + case DataBuffer.TYPE_USHORT: + d = new DataBufferUShort((int)(Math.ceil(fw/(16/bitsPerBand)))*h); + break; + + case DataBuffer.TYPE_INT: + d = new DataBufferInt((int)(Math.ceil(fw/(32/bitsPerBand)))*h); + break; + + default: + throw new IllegalArgumentException("Unsupported data type " + + dataType); + } + + return createPackedRaster(d, w, h, bitsPerBand, location); + } + } + + /** + * Creates a Raster based on a PixelInterleavedSampleModel with the + * specified DataBuffer, width, height, scanline stride, pixel + * stride, and band offsets. The number of bands is inferred from + * bandOffsets.length. The upper left corner of the Raster + * is given by the location argument. If location is null, (0, 0) + * will be used. + *

Note that interleaved DataBuffer.TYPE_INT + * Rasters are not supported. To create a 1-band Raster of type + * DataBuffer.TYPE_INT, use + * Raster.createPackedRaster(). + * @param dataBuffer the DataBuffer that contains the + * image data + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param scanlineStride the line stride of the image data + * @param pixelStride the pixel stride of the image data + * @param bandOffsets the offsets of all bands + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified + * DataBuffer, width, height, scanline stride, + * pixel stride and band offsets. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + * @throws IllegalArgumentException if dataType is not + * one of the supported data types, which are + * DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT + * @throws RasterFormatException if dataBuffer has more + * than one bank. + * @throws NullPointerException if dataBuffer is null + */ + public static WritableRaster createInterleavedRaster(DataBuffer dataBuffer, + int w, int h, + int scanlineStride, + int pixelStride, + int bandOffsets[], + Point location) { + if (dataBuffer == null) { + throw new NullPointerException("DataBuffer cannot be null"); + } + if (location == null) { + location = new Point(0, 0); + } + int dataType = dataBuffer.getDataType(); + + PixelInterleavedSampleModel csm = + new PixelInterleavedSampleModel(dataType, w, h, + pixelStride, + scanlineStride, + bandOffsets); + switch(dataType) { + case DataBuffer.TYPE_BYTE: + return new ByteInterleavedRaster(csm, dataBuffer, location); + + case DataBuffer.TYPE_USHORT: + return new ShortInterleavedRaster(csm, dataBuffer, location); + + default: + throw new IllegalArgumentException("Unsupported data type " + + dataType); + } + } + + /** + * Creates a Raster based on a BandedSampleModel with the + * specified DataBuffer, width, height, scanline stride, bank + * indices, and band offsets. The number of bands is inferred + * from bankIndices.length and bandOffsets.length, which must be + * the same. The upper left corner of the Raster is given by the + * location argument. If location is null, (0, 0) will be used. + * @param dataBuffer the DataBuffer that contains the + * image data + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param scanlineStride the line stride of the image data + * @param bankIndices the bank indices for each band + * @param bandOffsets the offsets of all bands + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified + * DataBuffer, width, height, scanline stride, + * bank indices and band offsets. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + * @throws IllegalArgumentException if dataType is not + * one of the supported data types, which are + * DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT + * or DataBuffer.TYPE_INT + * @throws NullPointerException if dataBuffer is null + */ + public static WritableRaster createBandedRaster(DataBuffer dataBuffer, + int w, int h, + int scanlineStride, + int bankIndices[], + int bandOffsets[], + Point location) { + if (dataBuffer == null) { + throw new NullPointerException("DataBuffer cannot be null"); + } + if (location == null) { + location = new Point(0,0); + } + int dataType = dataBuffer.getDataType(); + + int bands = bankIndices.length; + if (bandOffsets.length != bands) { + throw new IllegalArgumentException( + "bankIndices.length != bandOffsets.length"); + } + + BandedSampleModel bsm = + new BandedSampleModel(dataType, w, h, + scanlineStride, + bankIndices, bandOffsets); + + switch(dataType) { + case DataBuffer.TYPE_BYTE: + return new ByteBandedRaster(bsm, dataBuffer, location); + + case DataBuffer.TYPE_USHORT: + return new ShortBandedRaster(bsm, dataBuffer, location); + + case DataBuffer.TYPE_INT: + return new SunWritableRaster(bsm, dataBuffer, location); + + default: + throw new IllegalArgumentException("Unsupported data type " + + dataType); + } + } + + /** + * Creates a Raster based on a SinglePixelPackedSampleModel with + * the specified DataBuffer, width, height, scanline stride, and + * band masks. The number of bands is inferred from bandMasks.length. + * The upper left corner of the Raster is given by + * the location argument. If location is null, (0, 0) will be used. + * @param dataBuffer the DataBuffer that contains the + * image data + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param scanlineStride the line stride of the image data + * @param bandMasks an array containing an entry for each band + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified + * DataBuffer, width, height, scanline stride, + * and band masks. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + * @throws IllegalArgumentException if dataType is not + * one of the supported data types, which are + * DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT + * or DataBuffer.TYPE_INT + * @throws RasterFormatException if dataBuffer has more + * than one bank. + * @throws NullPointerException if dataBuffer is null + */ + public static WritableRaster createPackedRaster(DataBuffer dataBuffer, + int w, int h, + int scanlineStride, + int bandMasks[], + Point location) { + if (dataBuffer == null) { + throw new NullPointerException("DataBuffer cannot be null"); + } + if (location == null) { + location = new Point(0,0); + } + int dataType = dataBuffer.getDataType(); + + SinglePixelPackedSampleModel sppsm = + new SinglePixelPackedSampleModel(dataType, w, h, scanlineStride, + bandMasks); + + switch(dataType) { + case DataBuffer.TYPE_BYTE: + return new ByteInterleavedRaster(sppsm, dataBuffer, location); + + case DataBuffer.TYPE_USHORT: + return new ShortInterleavedRaster(sppsm, dataBuffer, location); + + case DataBuffer.TYPE_INT: + return new IntegerInterleavedRaster(sppsm, dataBuffer, location); + + default: + throw new IllegalArgumentException("Unsupported data type " + + dataType); + } + } + + /** + * Creates a Raster based on a MultiPixelPackedSampleModel with the + * specified DataBuffer, width, height, and bits per pixel. The upper + * left corner of the Raster is given by the location argument. If + * location is null, (0, 0) will be used. + * @param dataBuffer the DataBuffer that contains the + * image data + * @param w the width in pixels of the image data + * @param h the height in pixels of the image data + * @param bitsPerPixel the number of bits for each pixel + * @param location the upper-left corner of the Raster + * @return a WritableRaster object with the specified + * DataBuffer, width, height, and + * bits per pixel. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * location.x + w or + * location.y + h results in integer + * overflow + * @throws IllegalArgumentException if dataType is not + * one of the supported data types, which are + * DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT + * or DataBuffer.TYPE_INT + * @throws RasterFormatException if dataBuffer has more + * than one bank. + * @throws NullPointerException if dataBuffer is null + */ + public static WritableRaster createPackedRaster(DataBuffer dataBuffer, + int w, int h, + int bitsPerPixel, + Point location) { + if (dataBuffer == null) { + throw new NullPointerException("DataBuffer cannot be null"); + } + if (location == null) { + location = new Point(0,0); + } + int dataType = dataBuffer.getDataType(); + + if (dataType != DataBuffer.TYPE_BYTE && + dataType != DataBuffer.TYPE_USHORT && + dataType != DataBuffer.TYPE_INT) { + throw new IllegalArgumentException("Unsupported data type " + + dataType); + } + + if (dataBuffer.getNumBanks() != 1) { + throw new + RasterFormatException("DataBuffer for packed Rasters"+ + " must only have 1 bank."); + } + + MultiPixelPackedSampleModel mppsm = + new MultiPixelPackedSampleModel(dataType, w, h, bitsPerPixel); + + if (dataType == DataBuffer.TYPE_BYTE && + (bitsPerPixel == 1 || bitsPerPixel == 2 || bitsPerPixel == 4)) { + return new BytePackedRaster(mppsm, dataBuffer, location); + } else { + return new SunWritableRaster(mppsm, dataBuffer, location); + } + } + + + /** + * Creates a Raster with the specified SampleModel and DataBuffer. + * The upper left corner of the Raster is given by the location argument. + * If location is null, (0, 0) will be used. + * @param sm the specified SampleModel + * @param db the specified DataBuffer + * @param location the upper-left corner of the Raster + * @return a Raster with the specified + * SampleModel, DataBuffer, and + * location. + * @throws RasterFormatException if computing either + * location.x + sm.getWidth() or + * location.y + sm.getHeight() results in integer + * overflow + * @throws RasterFormatException if db has more + * than one bank and sm is a + * PixelInterleavedSampleModel, SinglePixelPackedSampleModel, + * or MultiPixelPackedSampleModel. + * @throws NullPointerException if either SampleModel or DataBuffer is + * null + */ + public static Raster createRaster(SampleModel sm, + DataBuffer db, + Point location) { + if ((sm == null) || (db == null)) { + throw new NullPointerException("SampleModel and DataBuffer cannot be null"); + } + + if (location == null) { + location = new Point(0,0); + } + int dataType = sm.getDataType(); + + if (sm instanceof PixelInterleavedSampleModel) { + switch(dataType) { + case DataBuffer.TYPE_BYTE: + return new ByteInterleavedRaster(sm, db, location); + + case DataBuffer.TYPE_USHORT: + return new ShortInterleavedRaster(sm, db, location); + } + } else if (sm instanceof SinglePixelPackedSampleModel) { + switch(dataType) { + case DataBuffer.TYPE_BYTE: + return new ByteInterleavedRaster(sm, db, location); + + case DataBuffer.TYPE_USHORT: + return new ShortInterleavedRaster(sm, db, location); + + case DataBuffer.TYPE_INT: + return new IntegerInterleavedRaster(sm, db, location); + } + } else if (sm instanceof MultiPixelPackedSampleModel && + dataType == DataBuffer.TYPE_BYTE && + sm.getSampleSize(0) < 8) { + return new BytePackedRaster(sm, db, location); + } + + // we couldn't do anything special - do the generic thing + + return new Raster(sm,db,location); + } + + /** + * Creates a WritableRaster with the specified SampleModel. + * The upper left corner of the Raster is given by the location argument. + * If location is null, (0, 0) will be used. + * @param sm the specified SampleModel + * @param location the upper-left corner of the + * WritableRaster + * @return a WritableRaster with the specified + * SampleModel and location. + * @throws RasterFormatException if computing either + * location.x + sm.getWidth() or + * location.y + sm.getHeight() results in integer + * overflow + */ + public static WritableRaster createWritableRaster(SampleModel sm, + Point location) { + if (location == null) { + location = new Point(0,0); + } + + return createWritableRaster(sm, sm.createDataBuffer(), location); + } + + /** + * Creates a WritableRaster with the specified SampleModel and DataBuffer. + * The upper left corner of the Raster is given by the location argument. + * If location is null, (0, 0) will be used. + * @param sm the specified SampleModel + * @param db the specified DataBuffer + * @param location the upper-left corner of the + * WritableRaster + * @return a WritableRaster with the specified + * SampleModel, DataBuffer, and + * location. + * @throws RasterFormatException if computing either + * location.x + sm.getWidth() or + * location.y + sm.getHeight() results in integer + * overflow + * @throws RasterFormatException if db has more + * than one bank and sm is a + * PixelInterleavedSampleModel, SinglePixelPackedSampleModel, + * or MultiPixelPackedSampleModel. + * @throws NullPointerException if either SampleModel or DataBuffer is null + */ + public static WritableRaster createWritableRaster(SampleModel sm, + DataBuffer db, + Point location) { + if ((sm == null) || (db == null)) { + throw new NullPointerException("SampleModel and DataBuffer cannot be null"); + } + if (location == null) { + location = new Point(0,0); + } + + int dataType = sm.getDataType(); + + if (sm instanceof PixelInterleavedSampleModel) { + switch(dataType) { + case DataBuffer.TYPE_BYTE: + return new ByteInterleavedRaster(sm, db, location); + + case DataBuffer.TYPE_USHORT: + return new ShortInterleavedRaster(sm, db, location); + } + } else if (sm instanceof SinglePixelPackedSampleModel) { + switch(dataType) { + case DataBuffer.TYPE_BYTE: + return new ByteInterleavedRaster(sm, db, location); + + case DataBuffer.TYPE_USHORT: + return new ShortInterleavedRaster(sm, db, location); + + case DataBuffer.TYPE_INT: + return new IntegerInterleavedRaster(sm, db, location); + } + } else if (sm instanceof MultiPixelPackedSampleModel && + dataType == DataBuffer.TYPE_BYTE && + sm.getSampleSize(0) < 8) { + return new BytePackedRaster(sm, db, location); + } + + // we couldn't do anything special - do the generic thing + + return new SunWritableRaster(sm,db,location); + } + + /** + * Constructs a Raster with the given SampleModel. The Raster's + * upper left corner is origin and it is the same size as the + * SampleModel. A DataBuffer large enough to describe the + * Raster is automatically created. + * @param sampleModel The SampleModel that specifies the layout + * @param origin The Point that specified the origin + * @throws RasterFormatException if computing either + * origin.x + sampleModel.getWidth() or + * origin.y + sampleModel.getHeight() results in + * integer overflow + * @throws NullPointerException either sampleModel or + * origin is null + */ + protected Raster(SampleModel sampleModel, + Point origin) { + this(sampleModel, + sampleModel.createDataBuffer(), + new Rectangle(origin.x, + origin.y, + sampleModel.getWidth(), + sampleModel.getHeight()), + origin, + null); + } + + /** + * Constructs a Raster with the given SampleModel and DataBuffer. + * The Raster's upper left corner is origin and it is the same size + * as the SampleModel. The DataBuffer is not initialized and must + * be compatible with SampleModel. + * @param sampleModel The SampleModel that specifies the layout + * @param dataBuffer The DataBuffer that contains the image data + * @param origin The Point that specifies the origin + * @throws RasterFormatException if computing either + * origin.x + sampleModel.getWidth() or + * origin.y + sampleModel.getHeight() results in + * integer overflow + * @throws NullPointerException either sampleModel or + * origin is null + */ + protected Raster(SampleModel sampleModel, + DataBuffer dataBuffer, + Point origin) { + this(sampleModel, + dataBuffer, + new Rectangle(origin.x, + origin.y, + sampleModel.getWidth(), + sampleModel.getHeight()), + origin, + null); + } + + /** + * Constructs a Raster with the given SampleModel, DataBuffer, and + * parent. aRegion specifies the bounding rectangle of the new + * Raster. When translated into the base Raster's coordinate + * system, aRegion must be contained by the base Raster. + * (The base Raster is the Raster's ancestor which has no parent.) + * sampleModelTranslate specifies the sampleModelTranslateX and + * sampleModelTranslateY values of the new Raster. + * + * Note that this constructor should generally be called by other + * constructors or create methods, it should not be used directly. + * @param sampleModel The SampleModel that specifies the layout + * @param dataBuffer The DataBuffer that contains the image data + * @param aRegion The Rectangle that specifies the image area + * @param sampleModelTranslate The Point that specifies the translation + * from SampleModel to Raster coordinates + * @param parent The parent (if any) of this raster + * @throws NullPointerException if any of sampleModel, + * dataBuffer, aRegion or + * sampleModelTranslate is null + * @throws RasterFormatException if aRegion has width + * or height less than or equal to zero, or computing either + * aRegion.x + aRegion.width or + * aRegion.y + aRegion.height results in integer + * overflow + */ + protected Raster(SampleModel sampleModel, + DataBuffer dataBuffer, + Rectangle aRegion, + Point sampleModelTranslate, + Raster parent) { + + if ((sampleModel == null) || (dataBuffer == null) || + (aRegion == null) || (sampleModelTranslate == null)) { + throw new NullPointerException("SampleModel, dataBuffer, aRegion and " + + "sampleModelTranslate cannot be null"); + } + this.sampleModel = sampleModel; + this.dataBuffer = dataBuffer; + minX = aRegion.x; + minY = aRegion.y; + width = aRegion.width; + height = aRegion.height; + if (width <= 0 || height <= 0) { + throw new RasterFormatException("negative or zero " + + ((width <= 0) ? "width" : "height")); + } + if ((minX + width) < minX) { + throw new RasterFormatException( + "overflow condition for X coordinates of Raster"); + } + if ((minY + height) < minY) { + throw new RasterFormatException( + "overflow condition for Y coordinates of Raster"); + } + + sampleModelTranslateX = sampleModelTranslate.x; + sampleModelTranslateY = sampleModelTranslate.y; + + numBands = sampleModel.getNumBands(); + numDataElements = sampleModel.getNumDataElements(); + this.parent = parent; + } + + + /** + * Returns the parent Raster (if any) of this Raster or null. + * @return the parent Raster or null. + */ + public Raster getParent() { + return parent; + } + + /** + * Returns the X translation from the coordinate system of the + * SampleModel to that of the Raster. To convert a pixel's X + * coordinate from the Raster coordinate system to the SampleModel + * coordinate system, this value must be subtracted. + * @return the X translation from the coordinate space of the + * Raster's SampleModel to that of the Raster. + */ + final public int getSampleModelTranslateX() { + return sampleModelTranslateX; + } + + /** + * Returns the Y translation from the coordinate system of the + * SampleModel to that of the Raster. To convert a pixel's Y + * coordinate from the Raster coordinate system to the SampleModel + * coordinate system, this value must be subtracted. + * @return the Y translation from the coordinate space of the + * Raster's SampleModel to that of the Raster. + */ + final public int getSampleModelTranslateY() { + return sampleModelTranslateY; + } + + /** + * Create a compatible WritableRaster the same size as this Raster with + * the same SampleModel and a new initialized DataBuffer. + * @return a compatible WritableRaster with the same sample + * model and a new data buffer. + */ + public WritableRaster createCompatibleWritableRaster() { + return new SunWritableRaster(sampleModel, new Point(0,0)); + } + + /** + * Create a compatible WritableRaster with the specified size, a new + * SampleModel, and a new initialized DataBuffer. + * @param w the specified width of the new WritableRaster + * @param h the specified height of the new WritableRaster + * @return a compatible WritableRaster with the specified + * size and a new sample model and data buffer. + * @exception RasterFormatException if the width or height is less than + * or equal to zero. + */ + 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 SunWritableRaster(sm, new Point(0,0)); + } + + /** + * Create a compatible WritableRaster with location (minX, minY) + * and size (width, height) specified by rect, a + * new SampleModel, and a new initialized DataBuffer. + * @param rect a Rectangle that specifies the size and + * location of the WritableRaster + * @return a compatible WritableRaster with the specified + * size and location and a new sample model and data buffer. + * @throws RasterFormatException if rect has width + * or height less than or equal to zero, or computing either + * rect.x + rect.width or + * rect.y + rect.height results in integer + * overflow + * @throws NullPointerException if rect is null + */ + 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); + } + + /** + * Create a compatible WritableRaster with the specified + * location (minX, minY) and size (width, height), a + * new SampleModel, and a new initialized DataBuffer. + * @param x the X coordinate of the upper-left corner of + * the WritableRaster + * @param y the Y coordinate of the upper-left corner of + * the WritableRaster + * @param w the specified width of the WritableRaster + * @param h the specified height of the WritableRaster + * @return a compatible WritableRaster with the specified + * size and location and a new sample model and data buffer. + * @throws RasterFormatException if w or h + * is less than or equal to zero, or computing either + * x + w or + * y + h results in integer + * overflow + */ + 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); + } + + /** + * Create a Raster with the same size, SampleModel and DataBuffer + * as this one, but with a different location. The new Raster + * will possess a reference to the current Raster, accessible + * through its getParent() method. + * + * @param childMinX the X coordinate of the upper-left + * corner of the new Raster + * @param childMinY the Y coordinate of the upper-left + * corner of the new Raster + * @return a new Raster with the same size, SampleModel, + * and DataBuffer as this Raster, but with the + * specified location. + * @throws RasterFormatException if computing either + * childMinX + this.getWidth() or + * childMinY + this.getHeight() results in integer + * overflow + */ + public Raster createTranslatedChild(int childMinX, int childMinY) { + return createChild(minX,minY,width,height, + childMinX,childMinY,null); + } + + /** + * Returns a new Raster which shares all or part of this Raster's + * DataBuffer. The new Raster will possess a reference to the + * current Raster, accessible through its getParent() method. + * + *

The parentX, parentY, width and height parameters + * form a Rectangle in this Raster's coordinate space, + * indicating the area of pixels to be shared. An error will + * be thrown if this Rectangle is not contained with the bounds + * of the current Raster. + * + *

The new Raster may additionally be translated to a + * different coordinate system for the plane than that used by the current + * Raster. The childMinX and childMinY parameters give the new + * (x, y) coordinate of the upper-left pixel of the returned + * Raster; the coordinate (childMinX, childMinY) in the new Raster + * will map to the same pixel as the coordinate (parentX, parentY) + * in the current Raster. + * + *

The new Raster may be defined to contain only a subset of + * the bands of the current Raster, possibly reordered, by means + * of the bandList parameter. If bandList is null, it is taken to + * include all of the bands of the current Raster in their current + * order. + * + *

To create a new Raster that contains a subregion of the current + * Raster, but shares its coordinate system and bands, + * this method should be called with childMinX equal to parentX, + * childMinY equal to parentY, and bandList equal to null. + * + * @param parentX The X coordinate of the upper-left corner + * in this Raster's coordinates + * @param parentY The Y coordinate of the upper-left corner + * in this Raster's coordinates + * @param width Width of the region starting at (parentX, parentY) + * @param height Height of the region starting at (parentX, parentY). + * @param childMinX The X coordinate of the upper-left corner + * of the returned Raster + * @param childMinY The Y coordinate of the upper-left corner + * of the returned Raster + * @param bandList Array of band indices, or null to use all bands + * @return a new Raster. + * @exception RasterFormatException if the specified subregion is outside + * of the raster bounds. + * @throws RasterFormatException if width or + * height + * is less than or equal to zero, or computing any of + * parentX + width, parentY + height, + * childMinX + width, or + * childMinY + height results in integer + * overflow + */ + public Raster createChild(int parentX, int parentY, + int width, int height, + int childMinX, int childMinY, + int bandList[]) { + if (parentX < this.minX) { + throw new RasterFormatException("parentX lies outside raster"); + } + if (parentY < this.minY) { + throw new RasterFormatException("parentY lies outside raster"); + } + if ((parentX + width < parentX) || + (parentX + width > this.width + this.minX)) { + throw new RasterFormatException("(parentX + width) is outside raster"); + } + if ((parentY + height < parentY) || + (parentY + height > this.height + this.minY)) { + throw new RasterFormatException("(parentY + height) is outside raster"); + } + + SampleModel subSampleModel; + // Note: the SampleModel for the child Raster should have the same + // width and height as that for the parent, since it represents + // the physical layout of the pixel data. The child Raster's width + // and height represent a "virtual" view of the pixel data, so + // they may be different than those of the SampleModel. + if (bandList == null) { + subSampleModel = sampleModel; + } else { + subSampleModel = sampleModel.createSubsetSampleModel(bandList); + } + + int deltaX = childMinX - parentX; + int deltaY = childMinY - parentY; + + return new Raster(subSampleModel, getDataBuffer(), + new Rectangle(childMinX, childMinY, width, height), + new Point(sampleModelTranslateX + deltaX, + sampleModelTranslateY + deltaY), this); + } + + /** + * Returns the bounding Rectangle of this Raster. This function returns + * the same information as getMinX/MinY/Width/Height. + * @return the bounding box of this Raster. + */ + public Rectangle getBounds() { + return new Rectangle(minX, minY, width, height); + } + + /** Returns the minimum valid X coordinate of the Raster. + * @return the minimum x coordinate of this Raster. + */ + final public int getMinX() { + return minX; + } + + /** Returns the minimum valid Y coordinate of the Raster. + * @return the minimum y coordinate of this Raster. + */ + final public int getMinY() { + return minY; + } + + /** Returns the width in pixels of the Raster. + * @return the width of this Raster. + */ + final public int getWidth() { + return width; + } + + /** Returns the height in pixels of the Raster. + * @return the height of this Raster. + */ + final public int getHeight() { + return height; + } + + /** Returns the number of bands (samples per pixel) in this Raster. + * @return the number of bands of this Raster. + */ + final public int getNumBands() { + return numBands; + } + + /** + * Returns the number of data elements needed to transfer one pixel + * via the getDataElements and setDataElements methods. When pixels + * are transferred via these methods, they may be transferred in a + * packed or unpacked format, depending on the implementation of the + * underlying SampleModel. Using these methods, pixels are transferred + * as an array of getNumDataElements() elements of a primitive type given + * by getTransferType(). The TransferType may or may not be the same + * as the storage data type of the DataBuffer. + * @return the number of data elements. + */ + final public int getNumDataElements() { + return sampleModel.getNumDataElements(); + } + + /** + * Returns the TransferType used to transfer pixels via the + * getDataElements and setDataElements methods. When pixels + * are transferred via these methods, they may be transferred in a + * packed or unpacked format, depending on the implementation of the + * underlying SampleModel. Using these methods, pixels are transferred + * as an array of getNumDataElements() elements of a primitive type given + * by getTransferType(). The TransferType may or may not be the same + * as the storage data type of the DataBuffer. The TransferType will + * be one of the types defined in DataBuffer. + * @return this transfer type. + */ + final public int getTransferType() { + return sampleModel.getTransferType(); + } + + /** Returns the DataBuffer associated with this Raster. + * @return the DataBuffer of this Raster. + */ + public DataBuffer getDataBuffer() { + return dataBuffer; + } + + /** Returns the SampleModel that describes the layout of the image data. + * @return the SampleModel of this Raster. + */ + public SampleModel getSampleModel() { + return sampleModel; + } + + /** + * Returns data for a single pixel in a primitive array of type + * TransferType. For image data supported by the Java 2D(tm) API, + * this will be one of DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, + * DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, + * or DataBuffer.TYPE_DOUBLE. Data may be returned in a packed format, + * thus increasing efficiency for data transfers. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * A ClassCastException will be thrown if the input object is non null + * and references anything other than an array of TransferType. + * @see java.awt.image.SampleModel#getDataElements(int, int, Object, DataBuffer) + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param outData An object reference to an array of type defined by + * getTransferType() and length getNumDataElements(). + * If null, an array of appropriate type and size will be + * allocated + * @return An object reference to an array of type defined by + * getTransferType() with the requested pixel data. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if outData is too small to hold the output. + */ + public Object getDataElements(int x, int y, Object outData) { + return sampleModel.getDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, + outData, dataBuffer); + } + + /** + * Returns the pixel data for the specified rectangle of pixels in a + * primitive array of type TransferType. + * For image data supported by the Java 2D API, this + * will be one of DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, + * DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, + * or DataBuffer.TYPE_DOUBLE. Data may be returned in a packed format, + * thus increasing efficiency for data transfers. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * A ClassCastException will be thrown if the input object is non null + * and references anything other than an array of TransferType. + * @see java.awt.image.SampleModel#getDataElements(int, int, int, int, Object, DataBuffer) + * @param x The X coordinate of the upper-left pixel location + * @param y The Y coordinate of the upper-left pixel location + * @param w Width of the pixel rectangle + * @param h Height of the pixel rectangle + * @param outData An object reference to an array of type defined by + * getTransferType() and length w*h*getNumDataElements(). + * If null, an array of appropriate type and size will be + * allocated. + * @return An object reference to an array of type defined by + * getTransferType() with the requested pixel data. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if outData is too small to hold the output. + */ + public Object getDataElements(int x, int y, int w, int h, Object outData) { + return sampleModel.getDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, + w, h, outData, dataBuffer); + } + + /** + * Returns the samples in an array of int for the specified pixel. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param iArray An optionally preallocated int array + * @return the samples for the specified pixel. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if iArray is too small to hold the output. + */ + public int[] getPixel(int x, int y, int iArray[]) { + return sampleModel.getPixel(x - sampleModelTranslateX, + y - sampleModelTranslateY, + iArray, dataBuffer); + } + + /** + * Returns the samples in an array of float for the + * specified pixel. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param fArray An optionally preallocated float array + * @return the samples for the specified pixel. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if fArray is too small to hold the output. + */ + public float[] getPixel(int x, int y, float fArray[]) { + return sampleModel.getPixel(x - sampleModelTranslateX, + y - sampleModelTranslateY, + fArray, dataBuffer); + } + + /** + * Returns the samples in an array of double for the specified pixel. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param dArray An optionally preallocated double array + * @return the samples for the specified pixel. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if dArray is too small to hold the output. + */ + public double[] getPixel(int x, int y, double dArray[]) { + return sampleModel.getPixel(x - sampleModelTranslateX, + y - sampleModelTranslateY, + dArray, dataBuffer); + } + + /** + * Returns an int array containing all samples for a rectangle of pixels, + * one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the upper-left pixel location + * @param y The Y coordinate of the upper-left pixel location + * @param w Width of the pixel rectangle + * @param h Height of the pixel rectangle + * @param iArray An optionally pre-allocated int array + * @return the samples for the specified rectangle of pixels. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if iArray is too small to hold the output. + */ + public int[] getPixels(int x, int y, int w, int h, int iArray[]) { + return sampleModel.getPixels(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, + iArray, dataBuffer); + } + + /** + * Returns a float array containing all samples for a rectangle of pixels, + * one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param w Width of the pixel rectangle + * @param h Height of the pixel rectangle + * @param fArray An optionally pre-allocated float array + * @return the samples for the specified rectangle of pixels. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if fArray is too small to hold the output. + */ + public float[] getPixels(int x, int y, int w, int h, + float fArray[]) { + return sampleModel.getPixels(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, + fArray, dataBuffer); + } + + /** + * Returns a double array containing all samples for a rectangle of pixels, + * one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the upper-left pixel location + * @param y The Y coordinate of the upper-left pixel location + * @param w Width of the pixel rectangle + * @param h Height of the pixel rectangle + * @param dArray An optionally pre-allocated double array + * @return the samples for the specified rectangle of pixels. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if dArray is too small to hold the output. + */ + public double[] getPixels(int x, int y, int w, int h, + double dArray[]) { + return sampleModel.getPixels(x - sampleModelTranslateX, + y - sampleModelTranslateY, + w, h, dArray, dataBuffer); + } + + + /** + * Returns the sample in a specified band for the pixel located + * at (x,y) as an int. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to return + * @return the sample in the specified band for the pixel at the + * specified coordinate. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. + */ + public int getSample(int x, int y, int b) { + return sampleModel.getSample(x - sampleModelTranslateX, + y - sampleModelTranslateY, b, + dataBuffer); + } + + /** + * Returns the sample in a specified band + * for the pixel located at (x,y) as a float. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to return + * @return the sample in the specified band for the pixel at the + * specified coordinate. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. + */ + public float getSampleFloat(int x, int y, int b) { + return sampleModel.getSampleFloat(x - sampleModelTranslateX, + y - sampleModelTranslateY, b, + dataBuffer); + } + + /** + * Returns the sample in a specified band + * for a pixel located at (x,y) as a double. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param b The band to return + * @return the sample in the specified band for the pixel at the + * specified coordinate. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. + */ + public double getSampleDouble(int x, int y, int b) { + return sampleModel.getSampleDouble(x - sampleModelTranslateX, + y - sampleModelTranslateY, + b, dataBuffer); + } + + /** + * Returns the samples for a specified band for the specified rectangle + * of pixels in an int array, one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the upper-left pixel location + * @param y The Y coordinate of the upper-left pixel location + * @param w Width of the pixel rectangle + * @param h Height of the pixel rectangle + * @param b The band to return + * @param iArray An optionally pre-allocated int array + * @return the samples for the specified band for the specified + * rectangle of pixels. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds, or if iArray is too small to + * hold the output. + */ + public int[] getSamples(int x, int y, int w, int h, int b, + int iArray[]) { + return sampleModel.getSamples(x - sampleModelTranslateX, + y - sampleModelTranslateY, + w, h, b, iArray, + dataBuffer); + } + + /** + * Returns the samples for a specified band for the specified rectangle + * of pixels in a float array, one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the upper-left pixel location + * @param y The Y coordinate of the upper-left pixel location + * @param w Width of the pixel rectangle + * @param h Height of the pixel rectangle + * @param b The band to return + * @param fArray An optionally pre-allocated float array + * @return the samples for the specified band for the specified + * rectangle of pixels. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds, or if fArray is too small to + * hold the output. + */ + public float[] getSamples(int x, int y, int w, int h, int b, + float fArray[]) { + return sampleModel.getSamples(x - sampleModelTranslateX, + y - sampleModelTranslateY, + w, h, b, fArray, dataBuffer); + } + + /** + * Returns the samples for a specified band for a specified rectangle + * of pixels in a double array, one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown + * if the coordinates are not in bounds. However, explicit bounds + * checking is not guaranteed. + * @param x The X coordinate of the upper-left pixel location + * @param y The Y coordinate of the upper-left pixel location + * @param w Width of the pixel rectangle + * @param h Height of the pixel rectangle + * @param b The band to return + * @param dArray An optionally pre-allocated double array + * @return the samples for the specified band for the specified + * rectangle of pixels. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds, or if dArray is too small to + * hold the output. + */ + public double[] getSamples(int x, int y, int w, int h, int b, + double dArray[]) { + return sampleModel.getSamples(x - sampleModelTranslateX, + y - sampleModelTranslateY, + w, h, b, dArray, dataBuffer); + } + +} diff --git a/jdk/src/share/classes/java/awt/image/RenderedImage.java b/jdk/src/share/classes/java/awt/image/RenderedImage.java new file mode 100644 index 00000000000..a14aa413fae --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/RenderedImage.java @@ -0,0 +1,217 @@ +/* + * Portions Copyright 1997-2000 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; +import java.awt.Rectangle; +import java.util.Dictionary; +import java.util.Vector; + +/** + * RenderedImage is a common interface for objects which contain + * or can produce image data in the form of Rasters. The image + * data may be stored/produced as a single tile or a regular array + * of tiles. + */ + +public interface RenderedImage { + + /** + * Returns a vector of RenderedImages that are the immediate sources of + * image data for this RenderedImage. This method returns null if + * the RenderedImage object has no information about its immediate + * sources. It returns an empty Vector if the RenderedImage object has + * no immediate sources. + * @return a Vector of RenderedImage objects. + */ + Vector getSources(); + + /** + * Gets a property from the property set of this image. The set of + * properties and whether it is immutable is determined by the + * implementing class. This method returns + * java.awt.Image.UndefinedProperty if the specified property is + * not defined for this RenderedImage. + * @param name the name of the property + * @return the property indicated by the specified name. + * @see java.awt.Image#UndefinedProperty + */ + Object getProperty(String name); + + /** + * Returns an array of names recognized by + * {@link #getProperty(String) getProperty(String)} + * or null, if no property names are recognized. + * @return a String array containing all of the + * property names that getProperty(String) recognizes; + * or null if no property names are recognized. + */ + String[] getPropertyNames(); + + /** + * Returns the ColorModel associated with this image. All Rasters + * returned from this image will have this as their ColorModel. This + * can return null. + * @return the ColorModel of this image. + */ + ColorModel getColorModel(); + + /** + * Returns the SampleModel associated with this image. All Rasters + * returned from this image will have this as their SampleModel. + * @return the SampleModel of this image. + */ + SampleModel getSampleModel(); + + /** + * Returns the width of the RenderedImage. + * @return the width of this RenderedImage. + */ + int getWidth(); + + /** + * Returns the height of the RenderedImage. + * @return the height of this RenderedImage. + */ + int getHeight(); + + /** + * Returns the minimum X coordinate (inclusive) of the RenderedImage. + * @return the X coordinate of this RenderedImage. + */ + int getMinX(); + + /** + * Returns the minimum Y coordinate (inclusive) of the RenderedImage. + * @return the Y coordinate of this RenderedImage. + */ + int getMinY(); + + /** + * Returns the number of tiles in the X direction. + * @return the number of tiles in the X direction. + */ + int getNumXTiles(); + + /** + * Returns the number of tiles in the Y direction. + * @return the number of tiles in the Y direction. + */ + int getNumYTiles(); + + /** + * Returns the minimum tile index in the X direction. + * @return the minimum tile index in the X direction. + */ + int getMinTileX(); + + /** + * Returns the minimum tile index in the Y direction. + * @return the minimum tile index in the X direction. + */ + int getMinTileY(); + + /** + * Returns the tile width in pixels. All tiles must have the same + * width. + * @return the tile width in pixels. + */ + int getTileWidth(); + + /** + * Returns the tile height in pixels. All tiles must have the same + * height. + * @return the tile height in pixels. + */ + int getTileHeight(); + + /** + * Returns the X offset of the tile grid relative to the origin, + * i.e., the X coordinate of the upper-left pixel of tile (0, 0). + * (Note that tile (0, 0) may not actually exist.) + * @return the X offset of the tile grid relative to the origin. + */ + int getTileGridXOffset(); + + /** + * Returns the Y offset of the tile grid relative to the origin, + * i.e., the Y coordinate of the upper-left pixel of tile (0, 0). + * (Note that tile (0, 0) may not actually exist.) + * @return the Y offset of the tile grid relative to the origin. + */ + int getTileGridYOffset(); + + /** + * Returns tile (tileX, tileY). Note that tileX and tileY are indices + * into the tile array, not pixel locations. The Raster that is returned + * is live and will be updated if the image is changed. + * @param tileX the X index of the requested tile in the tile array + * @param tileY the Y index of the requested tile in the tile array + * @return the tile given the specified indices. + */ + Raster getTile(int tileX, int tileY); + + /** + * Returns the image as one large tile (for tile based + * images this will require fetching the whole image + * and copying the image data over). The Raster returned is + * a copy of the image data and will not be updated if the image + * is changed. + * @return the image as one large tile. + */ + Raster getData(); + + /** + * Computes and returns an arbitrary region of the RenderedImage. + * The Raster returned is a copy of the image data and will not + * be updated if the image is changed. + * @param rect the region of the RenderedImage to be returned. + * @return the region of the RenderedImage + * indicated by the specified Rectangle. + */ + Raster getData(Rectangle rect); + + /** + * Computes an arbitrary rectangular region of the RenderedImage + * and copies it into a caller-supplied WritableRaster. The region + * to be computed is determined from the bounds of the supplied + * WritableRaster. The supplied WritableRaster must have a + * SampleModel that is compatible with this image. If raster is null, + * an appropriate WritableRaster is created. + * @param raster a WritableRaster to hold the returned portion of the + * image, or null. + * @return a reference to the supplied or created WritableRaster. + */ + WritableRaster copyData(WritableRaster raster); +} diff --git a/jdk/src/share/classes/java/awt/image/SampleModel.java b/jdk/src/share/classes/java/awt/image/SampleModel.java new file mode 100644 index 00000000000..95bc6c06d3a --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/SampleModel.java @@ -0,0 +1,1393 @@ +/* + * Portions Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +/** + * This abstract class defines an interface for extracting samples of pixels + * in an image. All image data is expressed as a collection of pixels. + * Each pixel consists of a number of samples. A sample is a datum + * for one band of an image and a band consists of all samples of a + * particular type in an image. For example, a pixel might contain + * three samples representing its red, green and blue components. + * There are three bands in the image containing this pixel. One band + * consists of all the red samples from all pixels in the + * image. The second band consists of all the green samples and + * the remaining band consists of all of the blue samples. The pixel + * can be stored in various formats. For example, all samples from + * a particular band can be stored contiguously or all samples from a + * single pixel can be stored contiguously. + *

+ * Subclasses of SampleModel specify the types of samples they can + * represent (e.g. unsigned 8-bit byte, signed 16-bit short, etc.) + * and may specify how the samples are organized in memory. + * In the Java 2D(tm) API, built-in image processing operators may + * not operate on all possible sample types, but generally will work + * for unsigned integral samples of 16 bits or less. Some operators + * support a wider variety of sample types. + *

+ * A collection of pixels is represented as a Raster, which consists of + * a DataBuffer and a SampleModel. The SampleModel allows access to + * samples in the DataBuffer and may provide low-level information that + * a programmer can use to directly manipulate samples and pixels in the + * DataBuffer. + *

+ * This class is generally a fall back method for dealing with + * images. More efficient code will cast the SampleModel to the + * appropriate subclass and extract the information needed to directly + * manipulate pixels in the DataBuffer. + * + * @see java.awt.image.DataBuffer + * @see java.awt.image.Raster + * @see java.awt.image.ComponentSampleModel + * @see java.awt.image.PixelInterleavedSampleModel + * @see java.awt.image.BandedSampleModel + * @see java.awt.image.MultiPixelPackedSampleModel + * @see java.awt.image.SinglePixelPackedSampleModel + */ + +public abstract class SampleModel +{ + + /** Width in pixels of the region of image data that this SampleModel + * describes. + */ + protected int width; + + /** Height in pixels of the region of image data that this SampleModel + * describes. + */ + protected int height; + + /** Number of bands of the image data that this SampleModel describes. */ + protected int numBands; + + /** Data type of the DataBuffer storing the pixel data. + * @see java.awt.image.DataBuffer + */ + protected int dataType; + + static private native void initIDs(); + static { + ColorModel.loadLibraries(); + initIDs(); + } + + /** + * Constructs a SampleModel with the specified parameters. + * @param dataType The data type of the DataBuffer storing the pixel data. + * @param w The width (in pixels) of the region of image data. + * @param h The height (in pixels) of the region of image data. + * @param numBands The number of bands of the image data. + * @throws IllegalArgumentException if w or h + * is not greater than 0 + * @throws IllegalArgumentException if the product of w + * and h is greater than + * Integer.MAX_VALUE + * @throws IllegalArgumentException if dataType is not + * one of the supported data types + */ + public SampleModel(int dataType, int w, int h, int numBands) + { + float size = (float)w*h; + if (w <= 0 || h <= 0) { + throw new IllegalArgumentException("Width ("+w+") and height ("+ + h+") must be > 0"); + } + if (size >= Integer.MAX_VALUE) { + throw new IllegalArgumentException("Dimensions (width="+w+ + " height="+h+") are too large"); + } + + if (dataType < DataBuffer.TYPE_BYTE || + (dataType > DataBuffer.TYPE_DOUBLE && + dataType != DataBuffer.TYPE_UNDEFINED)) + { + throw new IllegalArgumentException("Unsupported dataType: "+ + dataType); + } + + if (numBands <= 0) { + throw new IllegalArgumentException("Number of bands must be > 0"); + } + + this.dataType = dataType; + this.width = w; + this.height = h; + this.numBands = numBands; + } + + /** Returns the width in pixels. + * @return the width in pixels of the region of image data + * that this SampleModel describes. + */ + final public int getWidth() { + return width; + } + + /** Returns the height in pixels. + * @return the height in pixels of the region of image data + * that this SampleModel describes. + */ + final public int getHeight() { + return height; + } + + /** Returns the total number of bands of image data. + * @return the number of bands of image data that this + * SampleModel describes. + */ + final public int getNumBands() { + return numBands; + } + + /** Returns the number of data elements needed to transfer a pixel + * via the getDataElements and setDataElements methods. When pixels + * are transferred via these methods, they may be transferred in a + * packed or unpacked format, depending on the implementation of the + * SampleModel. Using these methods, pixels are transferred as an + * array of getNumDataElements() elements of a primitive type given + * by getTransferType(). The TransferType may or may not be the same + * as the storage DataType. + * @return the number of data elements. + * @see #getDataElements(int, int, Object, DataBuffer) + * @see #getDataElements(int, int, int, int, Object, DataBuffer) + * @see #setDataElements(int, int, Object, DataBuffer) + * @see #setDataElements(int, int, int, int, Object, DataBuffer) + * @see #getTransferType + */ + public abstract int getNumDataElements(); + + /** Returns the data type of the DataBuffer storing the pixel data. + * @return the data type. + */ + final public int getDataType() { + return dataType; + } + + /** Returns the TransferType used to transfer pixels via the + * getDataElements and setDataElements methods. When pixels + * are transferred via these methods, they may be transferred in a + * packed or unpacked format, depending on the implementation of the + * SampleModel. Using these methods, pixels are transferred as an + * array of getNumDataElements() elements of a primitive type given + * by getTransferType(). The TransferType may or may not be the same + * as the storage DataType. The TransferType will be one of the types + * defined in DataBuffer. + * @return the transfer type. + * @see #getDataElements(int, int, Object, DataBuffer) + * @see #getDataElements(int, int, int, int, Object, DataBuffer) + * @see #setDataElements(int, int, Object, DataBuffer) + * @see #setDataElements(int, int, int, int, Object, DataBuffer) + * @see #getNumDataElements + * @see java.awt.image.DataBuffer + */ + public int getTransferType() { + return dataType; + } + + /** + * Returns the samples for a specified pixel in an int array, + * one sample per array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location + * @param y The Y coordinate of the pixel location + * @param iArray If non-null, returns the samples in this array + * @param data The DataBuffer containing the image data + * @return the samples for the specified pixel. + * @see #setPixel(int, int, int[], DataBuffer) + * + * @throws NullPointerException if data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if iArray is too small to hold the output. + */ + public int[] getPixel(int x, int y, int iArray[], DataBuffer data) { + + int pixels[]; + + if (iArray != null) + pixels = iArray; + else + pixels = new int[numBands]; + + for (int i=0; i + * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is described by + * SampleModel sm1, to DataBuffer db2, whose + * storage layout is described by SampleModel sm2. + * The transfer will generally be more efficient than using + * getPixel/setPixel. + *

+     *       SampleModel sm1, sm2;
+     *       DataBuffer db1, db2;
+     *       sm2.setDataElements(x, y, sm1.getDataElements(x, y, null, db1), db2);
+     * 
+ * Using getDataElements/setDataElements to transfer between two + * DataBuffer/SampleModel pairs is legitimate if the SampleModels have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * If obj is non-null, it should be a primitive array of type TransferType. + * Otherwise, a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if obj is non-null and is not large enough to hold + * the pixel data. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param obj If non-null, a primitive array in which to return + * the pixel data. + * @param data The DataBuffer containing the image data. + * @return the data elements for the specified pixel. + * @see #getNumDataElements + * @see #getTransferType + * @see java.awt.image.DataBuffer + * @see #setDataElements(int, int, Object, DataBuffer) + * + * @throws NullPointerException if data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if obj is too small to hold the output. + */ + public abstract Object getDataElements(int x, int y, + Object obj, DataBuffer data); + + /** + * Returns the pixel data for the specified rectangle of pixels in a + * primitive array of type TransferType. + * For image data supported by the Java 2D API, this + * will be one of DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, + * DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, + * or DataBuffer.TYPE_DOUBLE. Data may be returned in a packed format, + * thus increasing efficiency for data transfers. Generally, obj + * should be passed in as null, so that the Object will be created + * automatically and will be of the right primitive data type. + *

+ * The following code illustrates transferring data for a rectangular + * region of pixels from + * DataBuffer db1, whose storage layout is described by + * SampleModel sm1, to DataBuffer db2, whose + * storage layout is described by SampleModel sm2. + * The transfer will generally be more efficient than using + * getPixels/setPixels. + *

+     *       SampleModel sm1, sm2;
+     *       DataBuffer db1, db2;
+     *       sm2.setDataElements(x, y, w, h, sm1.getDataElements(x, y, w,
+     *                           h, null, db1), db2);
+     * 
+ * Using getDataElements/setDataElements to transfer between two + * DataBuffer/SampleModel pairs is legitimate if the SampleModels have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * If obj is non-null, it should be a primitive array of type TransferType. + * Otherwise, a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if obj is non-null and is not large enough to hold + * the pixel data. + * @param x The minimum X coordinate of the pixel rectangle. + * @param y The minimum Y coordinate of the pixel rectangle. + * @param w The width of the pixel rectangle. + * @param h The height of the pixel rectangle. + * @param obj If non-null, a primitive array in which to return + * the pixel data. + * @param data The DataBuffer containing the image data. + * @return the data elements for the specified region of pixels. + * @see #getNumDataElements + * @see #getTransferType + * @see #setDataElements(int, int, int, int, Object, DataBuffer) + * @see java.awt.image.DataBuffer + * + * @throws NullPointerException if data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if obj is too small to hold the output. + */ + public Object getDataElements(int x, int y, int w, int h, + Object obj, DataBuffer data) { + + int type = getTransferType(); + int numDataElems = getNumDataElements(); + int cnt = 0; + Object o = null; + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] btemp; + byte[] bdata; + + if (obj == null) + bdata = new byte[numDataElems*w*h]; + else + bdata = (byte[])obj; + + for (int i=y; i + * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is described by + * SampleModel sm1, to DataBuffer db2, whose + * storage layout is described by SampleModel sm2. + * The transfer will generally be more efficient than using + * getPixel/setPixel. + *

+     *       SampleModel sm1, sm2;
+     *       DataBuffer db1, db2;
+     *       sm2.setDataElements(x, y, sm1.getDataElements(x, y, null, db1),
+     *                           db2);
+     * 
+ * Using getDataElements/setDataElements to transfer between two + * DataBuffer/SampleModel pairs is legitimate if the SampleModels have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * obj must be a primitive array of type TransferType. Otherwise, + * a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if obj is not large enough to hold the pixel data. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param obj A primitive array containing pixel data. + * @param data The DataBuffer containing the image data. + * @see #getNumDataElements + * @see #getTransferType + * @see #getDataElements(int, int, Object, DataBuffer) + * @see java.awt.image.DataBuffer + * + * @throws NullPointerException if data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if obj is too small to hold the input. + */ + public abstract void setDataElements(int x, int y, + Object obj, DataBuffer data); + + /** + * Sets the data for a rectangle of pixels in the specified DataBuffer + * from a primitive array of type TransferType. For image data supported + * by the Java 2D API, this will be one of DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, + * DataBuffer.TYPE_FLOAT, or DataBuffer.TYPE_DOUBLE. Data in the array + * may be in a packed format, thus increasing efficiency for data + * transfers. + *

+ * The following code illustrates transferring data for a rectangular + * region of pixels from + * DataBuffer db1, whose storage layout is described by + * SampleModel sm1, to DataBuffer db2, whose + * storage layout is described by SampleModel sm2. + * The transfer will generally be more efficient than using + * getPixels/setPixels. + *

+     *       SampleModel sm1, sm2;
+     *       DataBuffer db1, db2;
+     *       sm2.setDataElements(x, y, w, h, sm1.getDataElements(x, y, w, h,
+     *                           null, db1), db2);
+     * 
+ * Using getDataElements/setDataElements to transfer between two + * DataBuffer/SampleModel pairs is legitimate if the SampleModels have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * obj must be a primitive array of type TransferType. Otherwise, + * a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if obj is not large enough to hold the pixel data. + * @param x The minimum X coordinate of the pixel rectangle. + * @param y The minimum Y coordinate of the pixel rectangle. + * @param w The width of the pixel rectangle. + * @param h The height of the pixel rectangle. + * @param obj A primitive array containing pixel data. + * @param data The DataBuffer containing the image data. + * @see #getNumDataElements + * @see #getTransferType + * @see #getDataElements(int, int, int, int, Object, DataBuffer) + * @see java.awt.image.DataBuffer + * + * @throws NullPointerException if data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if obj is too small to hold the input. + */ + public void setDataElements(int x, int y, int w, int h, + Object obj, DataBuffer data) { + + int cnt = 0; + Object o = null; + int type = getTransferType(); + int numDataElems = getNumDataElements(); + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] barray = (byte[])obj; + byte[] btemp = new byte[numDataElems]; + + for (int i=y; isetSample(int, int, int, DataBuffer) method using + * that int value. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param b The band to set. + * @param s The input sample as a float. + * @param data The DataBuffer containing the image data. + * @see #getSample(int, int, int, DataBuffer) + * + * @throws NullPointerException if data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. + */ + public void setSample(int x, int y, int b, + float s , + DataBuffer data) { + int sample = (int)s; + + setSample(x, y, b, sample, data); + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using a double for input. + * The default implementation of this method casts the input + * double sample to an int and then calls the + * setSample(int, int, int, DataBuffer) method using + * that int value. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param b The band to set. + * @param s The input sample as a double. + * @param data The DataBuffer containing the image data. + * @see #getSample(int, int, int, DataBuffer) + * + * @throws NullPointerException if data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. + */ + public void setSample(int x, int y, int b, + double s, + DataBuffer data) { + int sample = (int)s; + + setSample(x, y, b, sample, data); + } + + /** + * Sets the samples in the specified band for the specified rectangle + * of pixels from an int array containing one sample per array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w The width of the pixel rectangle. + * @param h The height of the pixel rectangle. + * @param b The band to set. + * @param iArray The input samples in an int array. + * @param data The DataBuffer containing the image data. + * @see #getSamples(int, int, int, int, int, int[], DataBuffer) + * + * @throws NullPointerException if iArray or data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds, or if iArray is too small to + * hold the input. + */ + public void setSamples(int x, int y, int w, int h, int b, + int iArray[], DataBuffer data) { + + int Offset=0; + + for (int i=y; i<(y+h); i++) { + for (int j=x; j<(x+w); j++) { + setSample(j, i, b, iArray[Offset++], data); + } + } + } + + /** + * Sets the samples in the specified band for the specified rectangle + * of pixels from a float array containing one sample per array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w The width of the pixel rectangle. + * @param h The height of the pixel rectangle. + * @param b The band to set. + * @param fArray The input samples in a float array. + * @param data The DataBuffer containing the image data. + * @see #getSamples(int, int, int, int, int, float[], DataBuffer) + * + * @throws NullPointerException if fArray or data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds, or if fArray is too small to + * hold the input. + */ + public void setSamples(int x, int y, int w, int h, int b, + float fArray[], DataBuffer data) { + int Offset=0; + + for (int i=y; i<(y+h); i++) { + for (int j=x; j<(x+w); j++) { + setSample(j, i, b, fArray[Offset++], data); + } + } + } + + /** + * Sets the samples in the specified band for the specified rectangle + * of pixels from a double array containing one sample per array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w The width of the pixel rectangle. + * @param h The height of the pixel rectangle. + * @param b The band to set. + * @param dArray The input samples in a double array. + * @param data The DataBuffer containing the image data. + * @see #getSamples(int, int, int, int, int, double[], DataBuffer) + * + * @throws NullPointerException if dArray or data is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds, or if dArray is too small to + * hold the input. + */ + public void setSamples(int x, int y, int w, int h, int b, + double dArray[], DataBuffer data) { + int Offset=0; + + for (int i=y; i<(y+h); i++) { + for (int j=x; j<(x+w); j++) { + setSample(j, i, b, dArray[Offset++], data); + } + } + } + + /** + * Creates a SampleModel which describes data in this SampleModel's + * format, but with a different width and height. + * @param w the width of the image data + * @param h the height of the image data + * @return a SampleModel describing the same image + * data as this SampleModel, but with a + * different size. + */ + public abstract SampleModel createCompatibleSampleModel(int w, int h); + + /** + * Creates a new SampleModel + * with a subset of the bands of this + * SampleModel. + * @param bands the subset of bands of this SampleModel + * @return a SampleModel with a subset of bands of this + * SampleModel. + */ + public abstract SampleModel createSubsetSampleModel(int bands[]); + + /** + * Creates a DataBuffer that corresponds to this SampleModel. + * The DataBuffer's width and height will match this SampleModel's. + * @return a DataBuffer corresponding to this + * SampleModel. + */ + public abstract DataBuffer createDataBuffer(); + + /** Returns the size in bits of samples for all bands. + * @return the size of samples for all bands. + */ + public abstract int[] getSampleSize(); + + /** Returns the size in bits of samples for the specified band. + * @param band the specified band + * @return the size of the samples of the specified band. + */ + public abstract int getSampleSize(int band); + +} diff --git a/jdk/src/share/classes/java/awt/image/SinglePixelPackedSampleModel.java b/jdk/src/share/classes/java/awt/image/SinglePixelPackedSampleModel.java new file mode 100644 index 00000000000..77f0ce8c9b8 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/SinglePixelPackedSampleModel.java @@ -0,0 +1,805 @@ +/* + * Portions Copyright 1997-2001 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; + +import java.util.Arrays; + +/** + * This class represents pixel data packed such that the N samples which make + * up a single pixel are stored in a single data array element, and each data + * data array element holds samples for only one pixel. + * This class supports + * {@link DataBuffer#TYPE_BYTE TYPE_BYTE}, + * {@link DataBuffer#TYPE_USHORT TYPE_USHORT}, + * {@link DataBuffer#TYPE_INT TYPE_INT} data types. + * All data array elements reside + * in the first bank of a DataBuffer. Accessor methods are provided so + * that the image data can be manipulated directly. Scanline stride is the + * number of data array elements between a given sample and the corresponding + * sample in the same column of the next scanline. Bit masks are the masks + * required to extract the samples representing the bands of the pixel. + * Bit offsets are the offsets in bits into the data array + * element of the samples representing the bands of the pixel. + *

+ * The following code illustrates extracting the bits of the sample + * representing band b for pixel x,y + * from DataBuffer data: + *

+ *      int sample = data.getElem(y * scanlineStride + x);
+ *      sample = (sample & bitMasks[b]) >>> bitOffsets[b];
+ * 
+ */ + +public class SinglePixelPackedSampleModel extends SampleModel +{ + /** Bit masks for all bands of the image data. */ + private int bitMasks[]; + + /** Bit Offsets for all bands of the image data. */ + private int bitOffsets[]; + + /** Bit sizes for all the bands of the image data. */ + private int bitSizes[]; + + /** Maximum bit size. */ + private int maxBitSize; + + /** Line stride of the region of image data described by this + * SinglePixelPackedSampleModel. + */ + private int scanlineStride; + + private static native void initIDs(); + static { + ColorModel.loadLibraries(); + initIDs(); + } + + /** + * Constructs a SinglePixelPackedSampleModel with bitMasks.length bands. + * Each sample is stored in a data array element in the position of + * its corresponding bit mask. Each bit mask must be contiguous and + * masks must not overlap. + * @param dataType The data type for storing samples. + * @param w The width (in pixels) of the region of the + * image data described. + * @param h The height (in pixels) of the region of the + * image data described. + * @param bitMasks The bit masks for all bands. + * @throws IllegalArgumentException if dataType is not + * either DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, or + * DataBuffer.TYPE_INT + */ + public SinglePixelPackedSampleModel(int dataType, int w, int h, + int bitMasks[]) { + this(dataType, w, h, w, bitMasks); + if (dataType != DataBuffer.TYPE_BYTE && + dataType != DataBuffer.TYPE_USHORT && + dataType != DataBuffer.TYPE_INT) { + throw new IllegalArgumentException("Unsupported data type "+ + dataType); + } + } + + /** + * Constructs a SinglePixelPackedSampleModel with bitMasks.length bands + * and a scanline stride equal to scanlineStride data array elements. + * Each sample is stored in a data array element in the position of + * its corresponding bit mask. Each bit mask must be contiguous and + * masks must not overlap. + * @param dataType The data type for storing samples. + * @param w The width (in pixels) of the region of + * image data described. + * @param h The height (in pixels) of the region of + * image data described. + * @param scanlineStride The line stride of the image data. + * @param bitMasks The bit masks for all bands. + * @throws IllegalArgumentException if w or + * h is not greater than 0 + * @throws IllegalArgumentException if any mask in + * bitMask is not contiguous + * @throws IllegalArgumentException if dataType is not + * either DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, or + * DataBuffer.TYPE_INT + */ + public SinglePixelPackedSampleModel(int dataType, int w, int h, + int scanlineStride, int bitMasks[]) { + super(dataType, w, h, bitMasks.length); + if (dataType != DataBuffer.TYPE_BYTE && + dataType != DataBuffer.TYPE_USHORT && + dataType != DataBuffer.TYPE_INT) { + throw new IllegalArgumentException("Unsupported data type "+ + dataType); + } + this.dataType = dataType; + this.bitMasks = (int[]) bitMasks.clone(); + this.scanlineStride = scanlineStride; + + this.bitOffsets = new int[numBands]; + this.bitSizes = new int[numBands]; + + this.maxBitSize = 0; + for (int i=0; i>> 1; + bitOffset++; + } + while ((mask & 1) == 1) { + mask = mask >>> 1; + bitSize++; + } + if (mask != 0) { + throw new IllegalArgumentException("Mask "+bitMasks[i]+ + " must be contiguous"); + } + } + bitOffsets[i] = bitOffset; + bitSizes[i] = bitSize; + if (bitSize > maxBitSize) { + maxBitSize = bitSize; + } + } + } + + /** + * Returns the number of data elements needed to transfer one pixel + * via the getDataElements and setDataElements methods. + * For a SinglePixelPackedSampleModel, this is one. + */ + public int getNumDataElements() { + return 1; + } + + /** + * Returns the size of the buffer (in data array elements) + * needed for a data buffer that matches this + * SinglePixelPackedSampleModel. + */ + private long getBufferSize() { + long size = scanlineStride * (height-1) + width; + return size; + } + + /** + * Creates a new SinglePixelPackedSampleModel with the specified + * width and height. The new SinglePixelPackedSampleModel will have the + * same storage data type and bit masks as this + * SinglePixelPackedSampleModel. + * @param w the width of the resulting SampleModel + * @param h the height of the resulting SampleModel + * @return a SinglePixelPackedSampleModel with the + * specified width and height. + * @throws IllegalArgumentException if w or + * h is not greater than 0 + */ + public SampleModel createCompatibleSampleModel(int w, int h) { + SampleModel sampleModel = new SinglePixelPackedSampleModel(dataType, w, h, + bitMasks); + return sampleModel; + } + + /** + * Creates a DataBuffer that corresponds to this + * SinglePixelPackedSampleModel. The DataBuffer's data type and size + * will be consistent with this SinglePixelPackedSampleModel. The + * DataBuffer will have a single bank. + */ + public DataBuffer createDataBuffer() { + DataBuffer dataBuffer = null; + + int size = (int)getBufferSize(); + switch (dataType) { + case DataBuffer.TYPE_BYTE: + dataBuffer = new DataBufferByte(size); + break; + case DataBuffer.TYPE_USHORT: + dataBuffer = new DataBufferUShort(size); + break; + case DataBuffer.TYPE_INT: + dataBuffer = new DataBufferInt(size); + break; + } + return dataBuffer; + } + + /** Returns the number of bits per sample for all bands. */ + public int[] getSampleSize() { + int mask; + int sampleSize[] = new int [numBands]; + for (int i=0; i>> bitOffsets[i]; + while ((mask & 1) != 0) { + sampleSize[i] ++; + mask = mask >>> 1; + } + } + + return sampleSize; + } + + /** Returns the number of bits per sample for the specified band. */ + public int getSampleSize(int band) { + int sampleSize = 0; + int mask = bitMasks[band] >>> bitOffsets[band]; + while ((mask & 1) != 0) { + sampleSize ++; + mask = mask >>> 1; + } + + return sampleSize; + } + + /** Returns the offset (in data array elements) of pixel (x,y). + * The data element containing pixel x,y + * can be retrieved from a DataBuffer data with a + * SinglePixelPackedSampleModel sppsm as: + *
+     *        data.getElem(sppsm.getOffset(x, y));
+     * 
+ * @param x the X coordinate of the specified pixel + * @param y the Y coordinate of the specified pixel + * @return the offset of the specified pixel. + */ + public int getOffset(int x, int y) { + int offset = y * scanlineStride + x; + return offset; + } + + /** Returns the bit offsets into the data array element representing + * a pixel for all bands. + * @return the bit offsets representing a pixel for all bands. + */ + public int [] getBitOffsets() { + return (int[])bitOffsets.clone(); + } + + /** Returns the bit masks for all bands. + * @return the bit masks for all bands. + */ + public int [] getBitMasks() { + return (int[])bitMasks.clone(); + } + + /** Returns the scanline stride of this SinglePixelPackedSampleModel. + * @return the scanline stride of this + * SinglePixelPackedSampleModel. + */ + public int getScanlineStride() { + return scanlineStride; + } + + /** + * This creates a new SinglePixelPackedSampleModel with a subset of the + * bands of this SinglePixelPackedSampleModel. The new + * SinglePixelPackedSampleModel can be used with any DataBuffer that the + * existing SinglePixelPackedSampleModel can be used with. The new + * SinglePixelPackedSampleModel/DataBuffer combination will represent + * an image with a subset of the bands of the original + * SinglePixelPackedSampleModel/DataBuffer combination. + * @exception RasterFormatException if the length of the bands argument is + * greater than the number of bands in + * the sample model. + */ + public SampleModel createSubsetSampleModel(int bands[]) { + if (bands.length > numBands) + throw new RasterFormatException("There are only " + + numBands + + " bands"); + int newBitMasks[] = new int[bands.length]; + for (int i=0; i + * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is described by + * SinglePixelPackedSampleModel sppsm1, to + * DataBuffer db2, whose storage layout is described by + * SinglePixelPackedSampleModel sppsm2. + * The transfer will generally be more efficient than using + * getPixel/setPixel. + *
+     *       SinglePixelPackedSampleModel sppsm1, sppsm2;
+     *       DataBufferInt db1, db2;
+     *       sppsm2.setDataElements(x, y, sppsm1.getDataElements(x, y, null,
+     *                              db1), db2);
+     * 
+ * Using getDataElements/setDataElements to transfer between two + * DataBuffer/SampleModel pairs is legitimate if the SampleModels have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * If obj is non-null, it should be a primitive array of type TransferType. + * Otherwise, a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if obj is non-null and is not large enough to hold + * the pixel data. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param obj If non-null, a primitive array in which to return + * the pixel data. + * @param data The DataBuffer containing the image data. + * @return the data for the specified pixel. + * @see #setDataElements(int, int, Object, DataBuffer) + */ + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int type = getTransferType(); + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] bdata; + + if (obj == null) + bdata = new byte[1]; + else + bdata = (byte[])obj; + + bdata[0] = (byte)data.getElem(y * scanlineStride + x); + + obj = (Object)bdata; + break; + + case DataBuffer.TYPE_USHORT: + + short[] sdata; + + if (obj == null) + sdata = new short[1]; + else + sdata = (short[])obj; + + sdata[0] = (short)data.getElem(y * scanlineStride + x); + + obj = (Object)sdata; + break; + + case DataBuffer.TYPE_INT: + + int[] idata; + + if (obj == null) + idata = new int[1]; + else + idata = (int[])obj; + + idata[0] = data.getElem(y * scanlineStride + x); + + obj = (Object)idata; + break; + } + + return obj; + } + + /** + * Returns all samples in for the specified pixel in an int array. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param iArray If non-null, returns the samples in this array + * @param data The DataBuffer containing the image data. + * @return all samples for the specified pixel. + * @see #setPixel(int, int, int[], DataBuffer) + */ + public int [] getPixel(int x, int y, int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int pixels[]; + if (iArray == null) { + pixels = new int [numBands]; + } else { + pixels = iArray; + } + + int value = data.getElem(y * scanlineStride + x); + for (int i=0; i>> bitOffsets[i]; + } + return pixels; + } + + /** + * Returns all samples for the specified rectangle of pixels in + * an int array, one sample per array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w The width of the pixel rectangle. + * @param h The height of the pixel rectangle. + * @param iArray If non-null, returns the samples in this array. + * @param data The DataBuffer containing the image data. + * @return all samples for the specified region of pixels. + * @see #setPixels(int, int, int, int, int[], DataBuffer) + */ + public int[] getPixels(int x, int y, int w, int h, + int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int pixels[]; + if (iArray != null) { + pixels = iArray; + } else { + pixels = new int [w*h*numBands]; + } + int lineOffset = y*scanlineStride + x; + int dstOffset = 0; + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int value = data.getElem(lineOffset+j); + for (int k=0; k < numBands; k++) { + pixels[dstOffset++] = + ((value & bitMasks[k]) >>> bitOffsets[k]); + } + } + lineOffset += scanlineStride; + } + return pixels; + } + + /** + * Returns as int the sample in a specified band for the pixel + * located at (x,y). + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param b The band to return. + * @param data The DataBuffer containing the image data. + * @return the sample in a specified band for the specified + * pixel. + * @see #setSample(int, int, int, int, DataBuffer) + */ + public int getSample(int x, int y, int b, DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int sample = data.getElem(y * scanlineStride + x); + return ((sample & bitMasks[b]) >>> bitOffsets[b]); + } + + /** + * Returns the samples for a specified band for the specified rectangle + * of pixels in an int array, one sample per array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w The width of the pixel rectangle. + * @param h The height of the pixel rectangle. + * @param b The band to return. + * @param iArray If non-null, returns the samples in this array. + * @param data The DataBuffer containing the image data. + * @return the samples for the specified band for the specified + * region of pixels. + * @see #setSamples(int, int, int, int, int, int[], DataBuffer) + */ + public int[] getSamples(int x, int y, int w, int h, int b, + int iArray[], DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int samples[]; + if (iArray != null) { + samples = iArray; + } else { + samples = new int [w*h]; + } + int lineOffset = y*scanlineStride + x; + int dstOffset = 0; + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int value = data.getElem(lineOffset+j); + samples[dstOffset++] = + ((value & bitMasks[b]) >>> bitOffsets[b]); + } + lineOffset += scanlineStride; + } + return samples; + } + + /** + * Sets the data for a single pixel in the specified DataBuffer from a + * primitive array of type TransferType. For a + * SinglePixelPackedSampleModel, only the first element of the array + * will hold valid data, and the type of the array must be the same as + * the storage data type of the SinglePixelPackedSampleModel. + *

+ * The following code illustrates transferring data for one pixel from + * DataBuffer db1, whose storage layout is described by + * SinglePixelPackedSampleModel sppsm1, + * to DataBuffer db2, whose storage layout is described by + * SinglePixelPackedSampleModel sppsm2. + * The transfer will generally be more efficient than using + * getPixel/setPixel. + *

+     *       SinglePixelPackedSampleModel sppsm1, sppsm2;
+     *       DataBufferInt db1, db2;
+     *       sppsm2.setDataElements(x, y, sppsm1.getDataElements(x, y, null,
+     *                              db1), db2);
+     * 
+ * Using getDataElements/setDataElements to transfer between two + * DataBuffer/SampleModel pairs is legitimate if the SampleModels have + * the same number of bands, corresponding bands have the same number of + * bits per sample, and the TransferTypes are the same. + *

+ * obj must be a primitive array of type TransferType. Otherwise, + * a ClassCastException is thrown. An + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if obj is not large enough to hold the pixel data. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param obj A primitive array containing pixel data. + * @param data The DataBuffer containing the image data. + * @see #getDataElements(int, int, Object, DataBuffer) + */ + public void setDataElements(int x, int y, Object obj, DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int type = getTransferType(); + + switch(type) { + + case DataBuffer.TYPE_BYTE: + + byte[] barray = (byte[])obj; + data.setElem(y*scanlineStride+x, ((int)barray[0])&0xff); + break; + + case DataBuffer.TYPE_USHORT: + + short[] sarray = (short[])obj; + data.setElem(y*scanlineStride+x, ((int)sarray[0])&0xffff); + break; + + case DataBuffer.TYPE_INT: + + int[] iarray = (int[])obj; + data.setElem(y*scanlineStride+x, iarray[0]); + break; + } + } + + /** + * Sets a pixel in the DataBuffer using an int array of samples for input. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param iArray The input samples in an int array. + * @param data The DataBuffer containing the image data. + * @see #getPixel(int, int, int[], DataBuffer) + */ + public void setPixel(int x, int y, + int iArray[], + DataBuffer data) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int lineOffset = y * scanlineStride + x; + int value = data.getElem(lineOffset); + for (int i=0; i < numBands; i++) { + value &= ~bitMasks[i]; + value |= ((iArray[i] << bitOffsets[i]) & bitMasks[i]); + } + data.setElem(lineOffset, value); + } + + /** + * Sets all samples for a rectangle of pixels from an int array containing + * one sample per array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w The width of the pixel rectangle. + * @param h The height of the pixel rectangle. + * @param iArray The input samples in an int array. + * @param data The DataBuffer containing the image data. + * @see #getPixels(int, int, int, int, int[], DataBuffer) + */ + public void setPixels(int x, int y, int w, int h, + int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int lineOffset = y*scanlineStride + x; + int srcOffset = 0; + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int value = data.getElem(lineOffset+j); + for (int k=0; k < numBands; k++) { + value &= ~bitMasks[k]; + int srcValue = iArray[srcOffset++]; + value |= ((srcValue << bitOffsets[k]) + & bitMasks[k]); + } + data.setElem(lineOffset+j, value); + } + lineOffset += scanlineStride; + } + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using an int for input. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param b The band to set. + * @param s The input sample as an int. + * @param data The DataBuffer containing the image data. + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, int s, + DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int value = data.getElem(y*scanlineStride + x); + value &= ~bitMasks[b]; + value |= (s << bitOffsets[b]) & bitMasks[b]; + data.setElem(y*scanlineStride + x,value); + } + + /** + * Sets the samples in the specified band for the specified rectangle + * of pixels from an int array containing one sample per array element. + * ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w The width of the pixel rectangle. + * @param h The height of the pixel rectangle. + * @param b The band to set. + * @param iArray The input samples in an int array. + * @param data The DataBuffer containing the image data. + * @see #getSamples(int, int, int, int, int, int[], DataBuffer) + */ + public void setSamples(int x, int y, int w, int h, int b, + int iArray[], DataBuffer data) { + // Bounds check for 'b' will be performed automatically + if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + int lineOffset = y*scanlineStride + x; + int srcOffset = 0; + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int value = data.getElem(lineOffset+j); + value &= ~bitMasks[b]; + int sample = iArray[srcOffset++]; + value |= ((int)sample << bitOffsets[b]) & bitMasks[b]; + data.setElem(lineOffset+j,value); + } + lineOffset += scanlineStride; + } + } + + public boolean equals(Object o) { + if ((o == null) || !(o instanceof SinglePixelPackedSampleModel)) { + return false; + } + + SinglePixelPackedSampleModel that = (SinglePixelPackedSampleModel)o; + return this.width == that.width && + this.height == that.height && + this.numBands == that.numBands && + this.dataType == that.dataType && + Arrays.equals(this.bitMasks, that.bitMasks) && + Arrays.equals(this.bitOffsets, that.bitOffsets) && + Arrays.equals(this.bitSizes, that.bitSizes) && + this.maxBitSize == that.maxBitSize && + this.scanlineStride == that.scanlineStride; + } + + // If we implement equals() we must also implement hashCode + public int hashCode() { + int hash = 0; + hash = width; + hash <<= 8; + hash ^= height; + hash <<= 8; + hash ^= numBands; + hash <<= 8; + hash ^= dataType; + hash <<= 8; + for (int i = 0; i < bitMasks.length; i++) { + hash ^= bitMasks[i]; + hash <<= 8; + } + for (int i = 0; i < bitOffsets.length; i++) { + hash ^= bitOffsets[i]; + hash <<= 8; + } + for (int i = 0; i < bitSizes.length; i++) { + hash ^= bitSizes[i]; + hash <<= 8; + } + hash ^= maxBitSize; + hash <<= 8; + hash ^= scanlineStride; + return hash; + } +} diff --git a/jdk/src/share/classes/java/awt/image/WritableRaster.java b/jdk/src/share/classes/java/awt/image/WritableRaster.java new file mode 100644 index 00000000000..25840ae336e --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/WritableRaster.java @@ -0,0 +1,741 @@ +/* + * Portions Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; +import java.awt.Rectangle; +import java.awt.Point; + +/** + * This class extends Raster to provide pixel writing capabilities. + * Refer to the class comment for Raster for descriptions of how + * a Raster stores pixels. + * + *

The constructors of this class are protected. To instantiate + * a WritableRaster, use one of the createWritableRaster factory methods + * in the Raster class. + */ +public class WritableRaster extends Raster { + + /** + * Constructs a WritableRaster with the given SampleModel. The + * WritableRaster's upper left corner is origin and it is the + * same size as the SampleModel. A DataBuffer large enough to + * describe the WritableRaster is automatically created. + * @param sampleModel The SampleModel that specifies the layout. + * @param origin The Point that specifies the origin. + * @throws RasterFormatException if computing either + * origin.x + sampleModel.getWidth() or + * origin.y + sampleModel.getHeight() results + * in integer overflow + */ + protected WritableRaster(SampleModel sampleModel, + Point origin) { + this(sampleModel, + sampleModel.createDataBuffer(), + new Rectangle(origin.x, + origin.y, + sampleModel.getWidth(), + sampleModel.getHeight()), + origin, + null); + } + + /** + * Constructs a WritableRaster with the given SampleModel and DataBuffer. + * The WritableRaster's upper left corner is origin and it is the same + * size as the SampleModel. The DataBuffer is not initialized and must + * be compatible with SampleModel. + * @param sampleModel The SampleModel that specifies the layout. + * @param dataBuffer The DataBuffer that contains the image data. + * @param origin The Point that specifies the origin. + * @throws RasterFormatException if computing either + * origin.x + sampleModel.getWidth() or + * origin.y + sampleModel.getHeight() results + * in integer overflow + */ + protected WritableRaster(SampleModel sampleModel, + DataBuffer dataBuffer, + Point origin) { + this(sampleModel, + dataBuffer, + new Rectangle(origin.x, + origin.y, + sampleModel.getWidth(), + sampleModel.getHeight()), + origin, + null); + } + + /** + * Constructs a WritableRaster with the given SampleModel, DataBuffer, + * and parent. aRegion specifies the bounding rectangle of the new + * Raster. When translated into the base Raster's coordinate + * system, aRegion must be contained by the base Raster. + * (The base Raster is the Raster's ancestor which has no parent.) + * sampleModelTranslate specifies the sampleModelTranslateX and + * sampleModelTranslateY values of the new Raster. + * + * Note that this constructor should generally be called by other + * constructors or create methods, it should not be used directly. + * @param sampleModel The SampleModel that specifies the layout. + * @param dataBuffer The DataBuffer that contains the image data. + * @param aRegion The Rectangle that specifies the image area. + * @param sampleModelTranslate The Point that specifies the translation + * from SampleModel to Raster coordinates. + * @param parent The parent (if any) of this raster. + * @throws RasterFormatException if aRegion has width + * or height less than or equal to zero, or computing either + * aRegion.x + aRegion.width or + * aRegion.y + aRegion.height results in integer + * overflow + */ + protected WritableRaster(SampleModel sampleModel, + DataBuffer dataBuffer, + Rectangle aRegion, + Point sampleModelTranslate, + WritableRaster parent){ + super(sampleModel,dataBuffer,aRegion,sampleModelTranslate,parent); + } + + /** Returns the parent WritableRaster (if any) of this WritableRaster, + * or else null. + * @return the parent of this WritableRaster, or + * null. + */ + public WritableRaster getWritableParent() { + return (WritableRaster)parent; + } + + /** + * Create a WritableRaster with the same size, SampleModel and DataBuffer + * as this one, but with a different location. The new WritableRaster + * will possess a reference to the current WritableRaster, accessible + * through its getParent() and getWritableParent() methods. + * + * @param childMinX X coord of the upper left corner of the new Raster. + * @param childMinY Y coord of the upper left corner of the new Raster. + * @return a WritableRaster the same as this one except + * for the specified location. + * @throws RasterFormatException if computing either + * childMinX + this.getWidth() or + * childMinY + this.getHeight() results in integer + * overflow + */ + public WritableRaster createWritableTranslatedChild(int childMinX, + int childMinY) { + return createWritableChild(minX,minY,width,height, + childMinX,childMinY,null); + } + + /** + * Returns a new WritableRaster which shares all or part of this + * WritableRaster's DataBuffer. The new WritableRaster will + * possess a reference to the current WritableRaster, accessible + * through its getParent() and getWritableParent() methods. + * + *

The parentX, parentY, width and height parameters form a + * Rectangle in this WritableRaster's coordinate space, indicating + * the area of pixels to be shared. An error will be thrown if + * this Rectangle is not contained with the bounds of the current + * WritableRaster. + * + *

The new WritableRaster may additionally be translated to a + * different coordinate system for the plane than that used by the current + * WritableRaster. The childMinX and childMinY parameters give + * the new (x, y) coordinate of the upper-left pixel of the + * returned WritableRaster; the coordinate (childMinX, childMinY) + * in the new WritableRaster will map to the same pixel as the + * coordinate (parentX, parentY) in the current WritableRaster. + * + *

The new WritableRaster may be defined to contain only a + * subset of the bands of the current WritableRaster, possibly + * reordered, by means of the bandList parameter. If bandList is + * null, it is taken to include all of the bands of the current + * WritableRaster in their current order. + * + *

To create a new WritableRaster that contains a subregion of + * the current WritableRaster, but shares its coordinate system + * and bands, this method should be called with childMinX equal to + * parentX, childMinY equal to parentY, and bandList equal to + * null. + * + * @param parentX X coordinate of the upper left corner in this + * WritableRaster's coordinates. + * @param parentY Y coordinate of the upper left corner in this + * WritableRaster's coordinates. + * @param w Width of the region starting at (parentX, parentY). + * @param h Height of the region starting at (parentX, parentY). + * @param childMinX X coordinate of the upper left corner of + * the returned WritableRaster. + * @param childMinY Y coordinate of the upper left corner of + * the returned WritableRaster. + * @param bandList Array of band indices, or null to use all bands. + * @return a WritableRaster sharing all or part of the + * DataBuffer of this WritableRaster. + * @exception RasterFormatException if the subregion is outside of the + * raster bounds. + * @throws RasterFormatException if w or + * h + * is less than or equal to zero, or computing any of + * parentX + w, parentY + h, + * childMinX + w, or + * childMinY + h results in integer + * overflow + */ + public WritableRaster createWritableChild(int parentX, int parentY, + int w, int h, + int childMinX, int childMinY, + int bandList[]) { + if (parentX < this.minX) { + throw new RasterFormatException("parentX lies outside raster"); + } + if (parentY < this.minY) { + throw new RasterFormatException("parentY lies outside raster"); + } + if ((parentX+w < parentX) || (parentX+w > this.width + this.minX)) { + throw new RasterFormatException("(parentX + width) is outside raster"); + } + if ((parentY+h < parentY) || (parentY+h > this.height + this.minY)) { + throw new RasterFormatException("(parentY + height) is outside raster"); + } + + SampleModel sm; + // Note: the SampleModel for the child Raster should have the same + // width and height as that for the parent, since it represents + // the physical layout of the pixel data. The child Raster's width + // and height represent a "virtual" view of the pixel data, so + // they may be different than those of the SampleModel. + if (bandList != null) { + sm = sampleModel.createSubsetSampleModel(bandList); + } + else { + sm = sampleModel; + } + + int deltaX = childMinX - parentX; + int deltaY = childMinY - parentY; + + return new WritableRaster(sm, + getDataBuffer(), + new Rectangle(childMinX,childMinY, + w, h), + new Point(sampleModelTranslateX+deltaX, + sampleModelTranslateY+deltaY), + this); + } + + /** + * Sets the data for a single pixel from a + * primitive array of type TransferType. For image data supported by + * the Java 2D(tm) API, this will be one of DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, + * DataBuffer.TYPE_FLOAT, or DataBuffer.TYPE_DOUBLE. Data in the array + * may be in a packed format, thus increasing efficiency for data + * transfers. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if inData is not large enough to hold the pixel data. + * However, explicit bounds checking is not guaranteed. + * A ClassCastException will be thrown if the input object is not null + * and references anything other than an array of TransferType. + * @see java.awt.image.SampleModel#setDataElements(int, int, Object, DataBuffer) + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param inData An object reference to an array of type defined by + * getTransferType() and length getNumDataElements() + * containing the pixel data to place at x,y. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if inData is too small to hold the input. + */ + public void setDataElements(int x, int y, Object inData) { + sampleModel.setDataElements(x-sampleModelTranslateX, + y-sampleModelTranslateY, + inData, dataBuffer); + } + + /** + * Sets the data for a rectangle of pixels from an input Raster. + * The input Raster must be compatible with this WritableRaster + * in that they must have the same number of bands, corresponding bands + * must have the same number of bits per sample, the TransferTypes + * and NumDataElements must be the same, and the packing used by + * the getDataElements/setDataElements must be identical. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param inRaster Raster containing data to place at x,y. + * + * @throws NullPointerException if inRaster is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds. + */ + public void setDataElements(int x, int y, Raster inRaster) { + int dstOffX = x+inRaster.getMinX(); + int dstOffY = y+inRaster.getMinY(); + int width = inRaster.getWidth(); + int height = inRaster.getHeight(); + if ((dstOffX < this.minX) || (dstOffY < this.minY) || + (dstOffX + width > this.minX + this.width) || + (dstOffY + height > this.minY + this.height)) { + throw new ArrayIndexOutOfBoundsException + ("Coordinate out of bounds!"); + } + + int srcOffX = inRaster.getMinX(); + int srcOffY = inRaster.getMinY(); + Object tdata = null; + + for (int startY=0; startY < height; startY++) { + tdata = inRaster.getDataElements(srcOffX, srcOffY+startY, + width, 1, tdata); + setDataElements(dstOffX, dstOffY+startY, + width, 1, tdata); + } + } + + /** + * Sets the data for a rectangle of pixels from a + * primitive array of type TransferType. For image data supported by + * the Java 2D API, this will be one of DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, + * DataBuffer.TYPE_FLOAT, or DataBuffer.TYPE_DOUBLE. Data in the array + * may be in a packed format, thus increasing efficiency for data + * transfers. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds, or if inData is not large enough to hold the pixel data. + * However, explicit bounds checking is not guaranteed. + * A ClassCastException will be thrown if the input object is not null + * and references anything other than an array of TransferType. + * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, Object, DataBuffer) + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w Width of the pixel rectangle. + * @param h Height of the pixel rectangle. + * @param inData An object reference to an array of type defined by + * getTransferType() and length w*h*getNumDataElements() + * containing the pixel data to place between x,y and + * x+w-1, y+h-1. + * + * @throws NullPointerException if inData is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if inData is too small to hold the input. + */ + public void setDataElements(int x, int y, int w, int h, Object inData) { + sampleModel.setDataElements(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w,h,inData,dataBuffer); + } + + /** + * Copies pixels from Raster srcRaster to this WritableRaster. Each pixel + * in srcRaster is copied to the same x,y address in this raster, unless + * the address falls outside the bounds of this raster. srcRaster + * must have the same number of bands as this WritableRaster. The + * copy is a simple copy of source samples to the corresponding destination + * samples. + *

+ * If all samples of both source and destination Rasters are of + * integral type and less than or equal to 32 bits in size, then calling + * this method is equivalent to executing the following code for all + * x,y addresses valid in both Rasters. + *

+     *       Raster srcRaster;
+     *       WritableRaster dstRaster;
+     *       for (int b = 0; b < srcRaster.getNumBands(); b++) {
+     *           dstRaster.setSample(x, y, b, srcRaster.getSample(x, y, b));
+     *       }
+     * 
+ * Thus, when copying an integral type source to an integral type + * destination, if the source sample size is greater than the destination + * sample size for a particular band, the high order bits of the source + * sample are truncated. If the source sample size is less than the + * destination size for a particular band, the high order bits of the + * destination are zero-extended or sign-extended depending on whether + * srcRaster's SampleModel treats the sample as a signed or unsigned + * quantity. + *

+ * When copying a float or double source to an integral type destination, + * each source sample is cast to the destination type. When copying an + * integral type source to a float or double destination, the source + * is first converted to a 32-bit int (if necessary), using the above + * rules for integral types, and then the int is cast to float or + * double. + *

+ * @param srcRaster The Raster from which to copy pixels. + * + * @throws NullPointerException if srcRaster is null. + */ + public void setRect(Raster srcRaster) { + setRect(0,0,srcRaster); + } + + /** + * Copies pixels from Raster srcRaster to this WritableRaster. + * For each (x, y) address in srcRaster, the corresponding pixel + * is copied to address (x+dx, y+dy) in this WritableRaster, + * unless (x+dx, y+dy) falls outside the bounds of this raster. + * srcRaster must have the same number of bands as this WritableRaster. + * The copy is a simple copy of source samples to the corresponding + * destination samples. For details, see + * {@link WritableRaster#setRect(Raster)}. + * + * @param dx The X translation factor from src space to dst space + * of the copy. + * @param dy The Y translation factor from src space to dst space + * of the copy. + * @param srcRaster The Raster from which to copy pixels. + * + * @throws NullPointerException if srcRaster is null. + */ + public void setRect(int dx, int dy, Raster srcRaster) { + int width = srcRaster.getWidth(); + int height = srcRaster.getHeight(); + int srcOffX = srcRaster.getMinX(); + int srcOffY = srcRaster.getMinY(); + int dstOffX = dx+srcOffX; + int dstOffY = dy+srcOffY; + + // Clip to this raster + if (dstOffX < this.minX) { + int skipX = this.minX - dstOffX; + width -= skipX; + srcOffX += skipX; + dstOffX = this.minX; + } + if (dstOffY < this.minY) { + int skipY = this.minY - dstOffY; + height -= skipY; + srcOffY += skipY; + dstOffY = this.minY; + } + if (dstOffX+width > this.minX+this.width) { + width = this.minX + this.width - dstOffX; + } + if (dstOffY+height > this.minY+this.height) { + height = this.minY + this.height - dstOffY; + } + + if (width <= 0 || height <= 0) { + return; + } + + switch (srcRaster.getSampleModel().getDataType()) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + int[] iData = null; + for (int startY=0; startY < height; startY++) { + // Grab one scanline at a time + iData = + srcRaster.getPixels(srcOffX, srcOffY+startY, width, 1, + iData); + setPixels(dstOffX, dstOffY+startY, width, 1, iData); + } + break; + + case DataBuffer.TYPE_FLOAT: + float[] fData = null; + for (int startY=0; startY < height; startY++) { + fData = + srcRaster.getPixels(srcOffX, srcOffY+startY, width, 1, + fData); + setPixels(dstOffX, dstOffY+startY, width, 1, fData); + } + break; + + case DataBuffer.TYPE_DOUBLE: + double[] dData = null; + for (int startY=0; startY < height; startY++) { + // Grab one scanline at a time + dData = + srcRaster.getPixels(srcOffX, srcOffY+startY, width, 1, + dData); + setPixels(dstOffX, dstOffY+startY, width, 1, dData); + } + break; + } + } + + /** + * Sets a pixel in the DataBuffer using an int array of samples for input. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param iArray The input samples in a int array. + * + * @throws NullPointerException if iArray is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if iArray is too small to hold the input. + */ + public void setPixel(int x, int y, int iArray[]) { + sampleModel.setPixel(x-sampleModelTranslateX,y-sampleModelTranslateY, + iArray,dataBuffer); + } + + /** + * Sets a pixel in the DataBuffer using a float array of samples for input. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param fArray The input samples in a float array. + * + * @throws NullPointerException if fArray is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if fArray is too small to hold the input. + */ + public void setPixel(int x, int y, float fArray[]) { + sampleModel.setPixel(x-sampleModelTranslateX,y-sampleModelTranslateY, + fArray,dataBuffer); + } + + /** + * Sets a pixel in the DataBuffer using a double array of samples for input. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param dArray The input samples in a double array. + * + * @throws NullPointerException if dArray is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if dArray is too small to hold the input. + */ + public void setPixel(int x, int y, double dArray[]) { + sampleModel.setPixel(x-sampleModelTranslateX,y-sampleModelTranslateY, + dArray,dataBuffer); + } + + /** + * Sets all samples for a rectangle of pixels from an int array containing + * one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w Width of the pixel rectangle. + * @param h Height of the pixel rectangle. + * @param iArray The input int pixel array. + * + * @throws NullPointerException if iArray is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if iArray is too small to hold the input. + */ + public void setPixels(int x, int y, int w, int h, int iArray[]) { + sampleModel.setPixels(x-sampleModelTranslateX,y-sampleModelTranslateY, + w,h,iArray,dataBuffer); + } + + /** + * Sets all samples for a rectangle of pixels from a float array containing + * one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w Width of the pixel rectangle. + * @param h Height of the pixel rectangle. + * @param fArray The input float pixel array. + * + * @throws NullPointerException if fArray is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if fArray is too small to hold the input. + */ + public void setPixels(int x, int y, int w, int h, float fArray[]) { + sampleModel.setPixels(x-sampleModelTranslateX,y-sampleModelTranslateY, + w,h,fArray,dataBuffer); + } + + /** + * Sets all samples for a rectangle of pixels from a double array containing + * one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w Width of the pixel rectangle. + * @param h Height of the pixel rectangle. + * @param dArray The input double pixel array. + * + * @throws NullPointerException if dArray is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates are not + * in bounds, or if dArray is too small to hold the input. + */ + public void setPixels(int x, int y, int w, int h, double dArray[]) { + sampleModel.setPixels(x-sampleModelTranslateX,y-sampleModelTranslateY, + w,h,dArray,dataBuffer); + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using an int for input. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param b The band to set. + * @param s The input sample. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. + */ + public void setSample(int x, int y, int b, int s) { + sampleModel.setSample(x-sampleModelTranslateX, + y-sampleModelTranslateY, b, s, + dataBuffer); + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using a float for input. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param b The band to set. + * @param s The input sample as a float. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. + */ + public void setSample(int x, int y, int b, float s){ + sampleModel.setSample(x-sampleModelTranslateX,y-sampleModelTranslateY, + b,s,dataBuffer); + } + + /** + * Sets a sample in the specified band for the pixel located at (x,y) + * in the DataBuffer using a double for input. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the pixel location. + * @param y The Y coordinate of the pixel location. + * @param b The band to set. + * @param s The input sample as a double. + * + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. + */ + public void setSample(int x, int y, int b, double s){ + sampleModel.setSample(x-sampleModelTranslateX,y-sampleModelTranslateY, + b,s,dataBuffer); + } + + /** + * Sets the samples in the specified band for the specified rectangle + * of pixels from an int array containing one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w Width of the pixel rectangle. + * @param h Height of the pixel rectangle. + * @param b The band to set. + * @param iArray The input int sample array. + * + * @throws NullPointerException if iArray is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds, or if iArray is too small to + * hold the input. + */ + public void setSamples(int x, int y, int w, int h, int b, + int iArray[]) { + sampleModel.setSamples(x-sampleModelTranslateX,y-sampleModelTranslateY, + w,h,b,iArray,dataBuffer); + } + + /** + * Sets the samples in the specified band for the specified rectangle + * of pixels from a float array containing one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w Width of the pixel rectangle. + * @param h Height of the pixel rectangle. + * @param b The band to set. + * @param fArray The input float sample array. + * + * @throws NullPointerException if fArray is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds, or if fArray is too small to + * hold the input. + */ + public void setSamples(int x, int y, int w, int h, int b, + float fArray[]) { + sampleModel.setSamples(x-sampleModelTranslateX,y-sampleModelTranslateY, + w,h,b,fArray,dataBuffer); + } + + /** + * Sets the samples in the specified band for the specified rectangle + * of pixels from a double array containing one sample per array element. + * An ArrayIndexOutOfBoundsException may be thrown if the coordinates are + * not in bounds. + * However, explicit bounds checking is not guaranteed. + * @param x The X coordinate of the upper left pixel location. + * @param y The Y coordinate of the upper left pixel location. + * @param w Width of the pixel rectangle. + * @param h Height of the pixel rectangle. + * @param b The band to set. + * @param dArray The input double sample array. + * + * @throws NullPointerException if dArray is null. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds, or if dArray is too small to + * hold the input. + */ + public void setSamples(int x, int y, int w, int h, int b, + double dArray[]) { + sampleModel.setSamples(x-sampleModelTranslateX,y-sampleModelTranslateY, + w,h,b,dArray,dataBuffer); + } + +} diff --git a/jdk/src/share/classes/java/awt/image/WritableRenderedImage.java b/jdk/src/share/classes/java/awt/image/WritableRenderedImage.java new file mode 100644 index 00000000000..4b81c21b528 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/WritableRenderedImage.java @@ -0,0 +1,151 @@ +/* + * Portions Copyright 1997-2000 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* **************************************************************** + ****************************************************************** + ****************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 + *** As an unpublished work pursuant to Title 17 of the United + *** States Code. All rights reserved. + ****************************************************************** + ****************************************************************** + ******************************************************************/ + +package java.awt.image; +import java.awt.Point; + +/** + * WriteableRenderedImage is a common interface for objects which + * contain or can produce image data in the form of Rasters and + * which can be modified and/or written over. The image + * data may be stored/produced as a single tile or a regular array + * of tiles. + *

+ * WritableRenderedImage provides notification to other interested + * objects when a tile is checked out for writing (via the + * getWritableTile method) and when the last writer of a particular + * tile relinquishes its access (via a call to releaseWritableTile). + * Additionally, it allows any caller to determine whether any tiles + * are currently checked out (via hasTileWriters), and to obtain a + * list of such tiles (via getWritableTileIndices, in the form of a Vector + * of Point objects). + *

+ * Objects wishing to be notified of changes in tile writability must + * implement the TileObserver interface, and are added by a + * call to addTileObserver. Multiple calls to + * addTileObserver for the same object will result in multiple + * notifications. An existing observer may reduce its notifications + * by calling removeTileObserver; if the observer had no + * notifications the operation is a no-op. + *

+ * It is necessary for a WritableRenderedImage to ensure that + * notifications occur only when the first writer acquires a tile and + * the last writer releases it. + * + */ + +public interface WritableRenderedImage extends RenderedImage +{ + + /** + * Adds an observer. If the observer is already present, + * it will receive multiple notifications. + * @param to the specified TileObserver + */ + public void addTileObserver(TileObserver to); + + /** + * Removes an observer. If the observer was not registered, + * nothing happens. If the observer was registered for multiple + * notifications, it will now be registered for one fewer. + * @param to the specified TileObserver + */ + public void removeTileObserver(TileObserver to); + + /** + * Checks out a tile for writing. + * + * The WritableRenderedImage is responsible for notifying all + * of its TileObservers when a tile goes from having + * no writers to having one writer. + * + * @param tileX the X index of the tile. + * @param tileY the Y index of the tile. + * @return a writable tile. + */ + public WritableRaster getWritableTile(int tileX, int tileY); + + /** + * Relinquishes the right to write to a tile. If the caller + * continues to write to the tile, the results are undefined. + * Calls to this method should only appear in matching pairs + * with calls to getWritableTile; any other use will lead + * to undefined results. + * + * The WritableRenderedImage is responsible for notifying all of + * its TileObservers when a tile goes from having one writer + * to having no writers. + * + * @param tileX the X index of the tile. + * @param tileY the Y index of the tile. + */ + public void releaseWritableTile(int tileX, int tileY); + + /** + * Returns whether a tile is currently checked out for writing. + * + * @param tileX the X index of the tile. + * @param tileY the Y index of the tile. + * @return true if specified tile is checked out + * for writing; false otherwise. + */ + public boolean isTileWritable(int tileX, int tileY); + + /** + * Returns an array of Point objects indicating which tiles + * are checked out for writing. Returns null if none are + * checked out. + * @return an array containing the locations of tiles that are + * checked out for writing. + */ + public Point[] getWritableTileIndices(); + + /** + * Returns whether any tile is checked out for writing. + * Semantically equivalent to (getWritableTileIndices() != null). + * @return true if any tiles are checked out for + * writing; false otherwise. + */ + public boolean hasTileWriters(); + + /** + * Sets a rect of the image to the contents of the Raster r, which is + * assumed to be in the same coordinate space as the WritableRenderedImage. + * The operation is clipped to the bounds of the WritableRenderedImage. + * @param r the specified Raster + */ + public void setData(Raster r); + +} diff --git a/jdk/src/share/classes/java/awt/image/renderable/ContextualRenderedImageFactory.java b/jdk/src/share/classes/java/awt/image/renderable/ContextualRenderedImageFactory.java new file mode 100644 index 00000000000..dd95fed7d6e --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/renderable/ContextualRenderedImageFactory.java @@ -0,0 +1,143 @@ +/* + * Portions Copyright 1998-2000 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* ******************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.image.renderable; +import java.awt.geom.Rectangle2D; +import java.awt.image.RenderedImage; + +/** + * ContextualRenderedImageFactory provides an interface for the + * functionality that may differ between instances of + * RenderableImageOp. Thus different operations on RenderableImages + * may be performed by a single class such as RenderedImageOp through + * the use of multiple instances of ContextualRenderedImageFactory. + * The name ContextualRenderedImageFactory is commonly shortened to + * "CRIF." + * + *

All operations that are to be used in a rendering-independent + * chain must implement ContextualRenderedImageFactory. + * + *

Classes that implement this interface must provide a + * constructor with no arguments. + */ +public interface ContextualRenderedImageFactory extends RenderedImageFactory { + + /** + * Maps the operation's output RenderContext into a RenderContext + * for each of the operation's sources. This is useful for + * operations that can be expressed in whole or in part simply as + * alterations in the RenderContext, such as an affine mapping, or + * operations that wish to obtain lower quality renderings of + * their sources in order to save processing effort or + * transmission bandwith. Some operations, such as blur, can also + * use this mechanism to avoid obtaining sources of higher quality + * than necessary. + * + * @param i the index of the source image. + * @param renderContext the RenderContext being applied to the operation. + * @param paramBlock a ParameterBlock containing the operation's + * sources and parameters. + * @param image the RenderableImage being rendered. + * @return a RenderContext for + * the source at the specified index of the parameters + * Vector contained in the specified ParameterBlock. + */ + RenderContext mapRenderContext(int i, + RenderContext renderContext, + ParameterBlock paramBlock, + RenderableImage image); + + /** + * Creates a rendering, given a RenderContext and a ParameterBlock + * containing the operation's sources and parameters. The output + * is a RenderedImage that takes the RenderContext into account to + * determine its dimensions and placement on the image plane. + * This method houses the "intelligence" that allows a + * rendering-independent operation to adapt to a specific + * RenderContext. + * + * @param renderContext The RenderContext specifying the rendering + * @param paramBlock a ParameterBlock containing the operation's + * sources and parameters + * @return a RenderedImage from the sources and parameters + * in the specified ParameterBlock and according to the + * rendering instructions in the specified RenderContext. + */ + RenderedImage create(RenderContext renderContext, + ParameterBlock paramBlock); + + /** + * Returns the bounding box for the output of the operation, + * performed on a given set of sources, in rendering-independent + * space. The bounds are returned as a Rectangle2D, that is, an + * axis-aligned rectangle with floating-point corner coordinates. + * + * @param paramBlock a ParameterBlock containing the operation's + * sources and parameters. + * @return a Rectangle2D specifying the rendering-independent + * bounding box of the output. + */ + Rectangle2D getBounds2D(ParameterBlock paramBlock); + + /** + * Gets the appropriate instance of the property specified by the name + * parameter. This method must determine which instance of a property to + * return when there are multiple sources that each specify the property. + * + * @param paramBlock a ParameterBlock containing the operation's + * sources and parameters. + * @param name a String naming the desired property. + * @return an object reference to the value of the property requested. + */ + Object getProperty(ParameterBlock paramBlock, String name); + + /** + * Returns a list of names recognized by getProperty. + * @return the list of property names. + */ + String[] getPropertyNames(); + + /** + * Returns true if successive renderings (that is, calls to + * create(RenderContext, ParameterBlock)) with the same arguments + * may produce different results. This method may be used to + * determine whether an existing rendering may be cached and + * reused. It is always safe to return true. + * @return true if successive renderings with the + * same arguments might produce different results; + * false otherwise. + */ + boolean isDynamic(); +} diff --git a/jdk/src/share/classes/java/awt/image/renderable/RenderContext.java b/jdk/src/share/classes/java/awt/image/renderable/RenderContext.java new file mode 100644 index 00000000000..1f733b5c833 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/renderable/RenderContext.java @@ -0,0 +1,272 @@ +/* + * Portions Copyright 1998-2000 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* ******************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.image.renderable; +import java.util.*; +import java.awt.geom.*; +import java.awt.*; +import java.awt.image.*; + +/** + * A RenderContext encapsulates the information needed to produce a + * specific rendering from a RenderableImage. It contains the area to + * be rendered specified in rendering-independent terms, the + * resolution at which the rendering is to be performed, and hints + * used to control the rendering process. + * + *

Users create RenderContexts and pass them to the + * RenderableImage via the createRendering method. Most of the methods of + * RenderContexts are not meant to be used directly by applications, + * but by the RenderableImage and operator classes to which it is + * passed. + * + *

The AffineTransform parameter passed into and out of this class + * are cloned. The RenderingHints and Shape parameters are not + * necessarily cloneable and are therefore only reference copied. + * Altering RenderingHints or Shape instances that are in use by + * instances of RenderContext may have undesired side effects. + */ +public class RenderContext implements Cloneable { + + /** Table of hints. May be null. */ + RenderingHints hints; + + /** Transform to convert user coordinates to device coordinates. */ + AffineTransform usr2dev; + + /** The area of interest. May be null. */ + Shape aoi; + + // Various constructors that allow different levels of + // specificity. If the Shape is missing the whole renderable area + // is assumed. If hints is missing no hints are assumed. + + /** + * Constructs a RenderContext with a given transform. + * The area of interest is supplied as a Shape, + * and the rendering hints are supplied as a RenderingHints object. + * + * @param usr2dev an AffineTransform. + * @param aoi a Shape representing the area of interest. + * @param hints a RenderingHints object containing rendering hints. + */ + public RenderContext(AffineTransform usr2dev, + Shape aoi, + RenderingHints hints) { + this.hints = hints; + this.aoi = aoi; + this.usr2dev = (AffineTransform)usr2dev.clone(); + } + + /** + * Constructs a RenderContext with a given transform. + * The area of interest is taken to be the entire renderable area. + * No rendering hints are used. + * + * @param usr2dev an AffineTransform. + */ + public RenderContext(AffineTransform usr2dev) { + this(usr2dev, null, null); + } + + /** + * Constructs a RenderContext with a given transform and rendering hints. + * The area of interest is taken to be the entire renderable area. + * + * @param usr2dev an AffineTransform. + * @param hints a RenderingHints object containing rendering hints. + */ + public RenderContext(AffineTransform usr2dev, RenderingHints hints) { + this(usr2dev, null, hints); + } + + /** + * Constructs a RenderContext with a given transform and area of interest. + * The area of interest is supplied as a Shape. + * No rendering hints are used. + * + * @param usr2dev an AffineTransform. + * @param aoi a Shape representing the area of interest. + */ + public RenderContext(AffineTransform usr2dev, Shape aoi) { + this(usr2dev, aoi, null); + } + + /** + * Gets the rendering hints of this RenderContext. + * @return a RenderingHints object that represents + * the rendering hints of this RenderContext. + * @see #setRenderingHints(RenderingHints) + */ + public RenderingHints getRenderingHints() { + return hints; + } + + /** + * Sets the rendering hints of this RenderContext. + * @param hints a RenderingHints object that represents + * the rendering hints to assign to this RenderContext. + * @see #getRenderingHints + */ + public void setRenderingHints(RenderingHints hints) { + this.hints = hints; + } + + /** + * Sets the current user-to-device AffineTransform contained + * in the RenderContext to a given transform. + * + * @param newTransform the new AffineTransform. + * @see #getTransform + */ + public void setTransform(AffineTransform newTransform) { + usr2dev = (AffineTransform)newTransform.clone(); + } + + /** + * Modifies the current user-to-device transform by prepending another + * transform. In matrix notation the operation is: + *

+     * [this] = [modTransform] x [this]
+     * 
+ * + * @param modTransform the AffineTransform to prepend to the + * current usr2dev transform. + * @since 1.3 + */ + public void preConcatenateTransform(AffineTransform modTransform) { + this.preConcetenateTransform(modTransform); + } + + /** + * Modifies the current user-to-device transform by prepending another + * transform. In matrix notation the operation is: + *
+     * [this] = [modTransform] x [this]
+     * 
+ * This method does the same thing as the preConcatenateTransform + * method. It is here for backward compatibility with previous releases + * which misspelled the method name. + * + * @param modTransform the AffineTransform to prepend to the + * current usr2dev transform. + * @deprecated replaced by + * preConcatenateTransform(AffineTransform). + */ + @Deprecated + public void preConcetenateTransform(AffineTransform modTransform) { + usr2dev.preConcatenate(modTransform); + } + + /** + * Modifies the current user-to-device transform by appending another + * transform. In matrix notation the operation is: + *
+     * [this] = [this] x [modTransform]
+     * 
+ * + * @param modTransform the AffineTransform to append to the + * current usr2dev transform. + * @since 1.3 + */ + public void concatenateTransform(AffineTransform modTransform) { + this.concetenateTransform(modTransform); + } + + /** + * Modifies the current user-to-device transform by appending another + * transform. In matrix notation the operation is: + *
+     * [this] = [this] x [modTransform]
+     * 
+ * This method does the same thing as the concatenateTransform + * method. It is here for backward compatibility with previous releases + * which misspelled the method name. + * + * @param modTransform the AffineTransform to append to the + * current usr2dev transform. + * @deprecated replaced by + * concatenateTransform(AffineTransform). + */ + @Deprecated + public void concetenateTransform(AffineTransform modTransform) { + usr2dev.concatenate(modTransform); + } + + /** + * Gets the current user-to-device AffineTransform. + * + * @return a reference to the current AffineTransform. + * @see #setTransform(AffineTransform) + */ + public AffineTransform getTransform() { + return (AffineTransform)usr2dev.clone(); + } + + /** + * Sets the current area of interest. The old area is discarded. + * + * @param newAoi The new area of interest. + * @see #getAreaOfInterest + */ + public void setAreaOfInterest(Shape newAoi) { + aoi = newAoi; + } + + /** + * Gets the ares of interest currently contained in the + * RenderContext. + * + * @return a reference to the area of interest of the RenderContext, + * or null if none is specified. + * @see #setAreaOfInterest(Shape) + */ + public Shape getAreaOfInterest() { + return aoi; + } + + /** + * Makes a copy of a RenderContext. The area of interest is copied + * by reference. The usr2dev AffineTransform and hints are cloned, + * while the area of interest is copied by reference. + * + * @return the new cloned RenderContext. + */ + public Object clone() { + RenderContext newRenderContext = new RenderContext(usr2dev, + aoi, hints); + return newRenderContext; + } +} diff --git a/jdk/src/share/classes/java/awt/image/renderable/RenderableImage.java b/jdk/src/share/classes/java/awt/image/renderable/RenderableImage.java new file mode 100644 index 00000000000..f4ee708a003 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/renderable/RenderableImage.java @@ -0,0 +1,198 @@ +/* + * Portions Copyright 1998-2000 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* ******************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.image.renderable; +import java.util.Vector; +import java.awt.RenderingHints; +import java.awt.image.*; + +/** + * A RenderableImage is a common interface for rendering-independent + * images (a notion which subsumes resolution independence). That is, + * images which are described and have operations applied to them + * independent of any specific rendering of the image. For example, a + * RenderableImage can be rotated and cropped in + * resolution-independent terms. Then, it can be rendered for various + * specific contexts, such as a draft preview, a high-quality screen + * display, or a printer, each in an optimal fashion. + * + *

A RenderedImage is returned from a RenderableImage via the + * createRendering() method, which takes a RenderContext. The + * RenderContext specifies how the RenderedImage should be + * constructed. Note that it is not possible to extract pixels + * directly from a RenderableImage. + * + *

The createDefaultRendering() and createScaledRendering() methods are + * convenience methods that construct an appropriate RenderContext + * internally. All of the rendering methods may return a reference to a + * previously produced rendering. + */ +public interface RenderableImage { + + /** + * String constant that can be used to identify a property on + * a RenderedImage obtained via the createRendering or + * createScaledRendering methods. If such a property exists, + * the value of the propoery will be a RenderingHints object + * specifying which hints were observed in creating the rendering. + */ + static final String HINTS_OBSERVED = "HINTS_OBSERVED"; + + /** + * Returns a vector of RenderableImages that are the sources of + * image data for this RenderableImage. Note that this method may + * return an empty vector, to indicate that the image has no sources, + * or null, to indicate that no information is available. + * + * @return a (possibly empty) Vector of RenderableImages, or null. + */ + Vector getSources(); + + /** + * Gets a property from the property set of this image. + * If the property name is not recognized, java.awt.Image.UndefinedProperty + * will be returned. + * + * @param name the name of the property to get, as a String. + * @return a reference to the property Object, or the value + * java.awt.Image.UndefinedProperty. + */ + Object getProperty(String name); + + /** + * Returns a list of names recognized by getProperty. + * @return a list of property names. + */ + String[] getPropertyNames(); + + /** + * Returns true if successive renderings (that is, calls to + * createRendering() or createScaledRendering()) with the same arguments + * may produce different results. This method may be used to + * determine whether an existing rendering may be cached and + * reused. It is always safe to return true. + * @return true if successive renderings with the + * same arguments might produce different results; + * false otherwise. + */ + boolean isDynamic(); + + /** + * Gets the width in user coordinate space. By convention, the + * usual width of a RenderableImage is equal to the image's aspect + * ratio (width divided by height). + * + * @return the width of the image in user coordinates. + */ + float getWidth(); + + /** + * Gets the height in user coordinate space. By convention, the + * usual height of a RenderedImage is equal to 1.0F. + * + * @return the height of the image in user coordinates. + */ + float getHeight(); + + /** + * Gets the minimum X coordinate of the rendering-independent image data. + * @return the minimum X coordinate of the rendering-independent image + * data. + */ + float getMinX(); + + /** + * Gets the minimum Y coordinate of the rendering-independent image data. + * @return the minimum Y coordinate of the rendering-independent image + * data. + */ + float getMinY(); + + /** + * Creates a RenderedImage instance of this image with width w, and + * height h in pixels. The RenderContext is built automatically + * with an appropriate usr2dev transform and an area of interest + * of the full image. All the rendering hints come from hints + * passed in. + * + *

If w == 0, it will be taken to equal + * Math.round(h*(getWidth()/getHeight())). + * Similarly, if h == 0, it will be taken to equal + * Math.round(w*(getHeight()/getWidth())). One of + * w or h must be non-zero or else an IllegalArgumentException + * will be thrown. + * + *

The created RenderedImage may have a property identified + * by the String HINTS_OBSERVED to indicate which RenderingHints + * were used to create the image. In addition any RenderedImages + * that are obtained via the getSources() method on the created + * RenderedImage may have such a property. + * + * @param w the width of rendered image in pixels, or 0. + * @param h the height of rendered image in pixels, or 0. + * @param hints a RenderingHints object containg hints. + * @return a RenderedImage containing the rendered data. + */ + RenderedImage createScaledRendering(int w, int h, RenderingHints hints); + + /** + * Returnd a RenderedImage instance of this image with a default + * width and height in pixels. The RenderContext is built + * automatically with an appropriate usr2dev transform and an area + * of interest of the full image. The rendering hints are + * empty. createDefaultRendering may make use of a stored + * rendering for speed. + * + * @return a RenderedImage containing the rendered data. + */ + RenderedImage createDefaultRendering(); + + /** + * Creates a RenderedImage that represented a rendering of this image + * using a given RenderContext. This is the most general way to obtain a + * rendering of a RenderableImage. + * + *

The created RenderedImage may have a property identified + * by the String HINTS_OBSERVED to indicate which RenderingHints + * (from the RenderContext) were used to create the image. + * In addition any RenderedImages + * that are obtained via the getSources() method on the created + * RenderedImage may have such a property. + * + * @param renderContext the RenderContext to use to produce the rendering. + * @return a RenderedImage containing the rendered data. + */ + RenderedImage createRendering(RenderContext renderContext); +} diff --git a/jdk/src/share/classes/java/awt/image/renderable/RenderableImageOp.java b/jdk/src/share/classes/java/awt/image/renderable/RenderableImageOp.java new file mode 100644 index 00000000000..a93043c6333 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/renderable/RenderableImageOp.java @@ -0,0 +1,350 @@ +/* + * Portions Copyright 1998-2000 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* ******************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.image.renderable; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.RenderedImage; +import java.awt.RenderingHints; +import java.util.Hashtable; +import java.util.Vector; + +/** + * This class handles the renderable aspects of an operation with help + * from its associated instance of a ContextualRenderedImageFactory. + */ +public class RenderableImageOp implements RenderableImage { + + /** A ParameterBlock containing source and parameters. */ + ParameterBlock paramBlock; + + /** The associated ContextualRenderedImageFactory. */ + ContextualRenderedImageFactory myCRIF; + + /** The bounding box of the results of this RenderableImageOp. */ + Rectangle2D boundingBox; + + + /** + * Constructs a RenderedImageOp given a + * ContextualRenderedImageFactory object, and + * a ParameterBlock containing RenderableImage sources and other + * parameters. Any RenderedImage sources referenced by the + * ParameterBlock will be ignored. + * + * @param CRIF a ContextualRenderedImageFactory object + * @param paramBlock a ParameterBlock containing this operation's source + * images and other parameters necessary for the operation + * to run. + */ + public RenderableImageOp(ContextualRenderedImageFactory CRIF, + ParameterBlock paramBlock) { + this.myCRIF = CRIF; + this.paramBlock = (ParameterBlock) paramBlock.clone(); + } + + /** + * Returns a vector of RenderableImages that are the sources of + * image data for this RenderableImage. Note that this method may + * return an empty vector, to indicate that the image has no sources, + * or null, to indicate that no information is available. + * + * @return a (possibly empty) Vector of RenderableImages, or null. + */ + public Vector getSources() { + return getRenderableSources(); + } + + private Vector getRenderableSources() { + Vector sources = null; + + if (paramBlock.getNumSources() > 0) { + sources = new Vector(); + int i = 0; + while (i < paramBlock.getNumSources()) { + Object o = paramBlock.getSource(i); + if (o instanceof RenderableImage) { + sources.add((RenderableImage)o); + i++; + } else { + break; + } + } + } + return sources; + } + + /** + * Gets a property from the property set of this image. + * If the property name is not recognized, java.awt.Image.UndefinedProperty + * will be returned. + * + * @param name the name of the property to get, as a String. + * @return a reference to the property Object, or the value + * java.awt.Image.UndefinedProperty. + */ + public Object getProperty(String name) { + return myCRIF.getProperty(paramBlock, name); + } + + /** + * Return a list of names recognized by getProperty. + * @return a list of property names. + */ + public String[] getPropertyNames() { + return myCRIF.getPropertyNames(); + } + + /** + * Returns true if successive renderings (that is, calls to + * createRendering() or createScaledRendering()) with the same arguments + * may produce different results. This method may be used to + * determine whether an existing rendering may be cached and + * reused. The CRIF's isDynamic method will be called. + * @return true if successive renderings with the + * same arguments might produce different results; + * false otherwise. + */ + public boolean isDynamic() { + return myCRIF.isDynamic(); + } + + /** + * Gets the width in user coordinate space. By convention, the + * usual width of a RenderableImage is equal to the image's aspect + * ratio (width divided by height). + * + * @return the width of the image in user coordinates. + */ + public float getWidth() { + if (boundingBox == null) { + boundingBox = myCRIF.getBounds2D(paramBlock); + } + return (float)boundingBox.getWidth(); + } + + /** + * Gets the height in user coordinate space. By convention, the + * usual height of a RenderedImage is equal to 1.0F. + * + * @return the height of the image in user coordinates. + */ + public float getHeight() { + if (boundingBox == null) { + boundingBox = myCRIF.getBounds2D(paramBlock); + } + return (float)boundingBox.getHeight(); + } + + /** + * Gets the minimum X coordinate of the rendering-independent image data. + */ + public float getMinX() { + if (boundingBox == null) { + boundingBox = myCRIF.getBounds2D(paramBlock); + } + return (float)boundingBox.getMinX(); + } + + /** + * Gets the minimum Y coordinate of the rendering-independent image data. + */ + public float getMinY() { + if (boundingBox == null) { + boundingBox = myCRIF.getBounds2D(paramBlock); + } + return (float)boundingBox.getMinY(); + } + + /** + * Change the current ParameterBlock of the operation, allowing + * editing of image rendering chains. The effects of such a + * change will be visible when a new rendering is created from + * this RenderableImageOp or any dependent RenderableImageOp. + * + * @param paramBlock the new ParameterBlock. + * @return the old ParameterBlock. + * @see #getParameterBlock + */ + public ParameterBlock setParameterBlock(ParameterBlock paramBlock) { + ParameterBlock oldParamBlock = this.paramBlock; + this.paramBlock = (ParameterBlock)paramBlock.clone(); + return oldParamBlock; + } + + /** + * Returns a reference to the current parameter block. + * @return the ParameterBlock of this + * RenderableImageOp. + * @see #setParameterBlock(ParameterBlock) + */ + public ParameterBlock getParameterBlock() { + return paramBlock; + } + + /** + * Creates a RenderedImage instance of this image with width w, and + * height h in pixels. The RenderContext is built automatically + * with an appropriate usr2dev transform and an area of interest + * of the full image. All the rendering hints come from hints + * passed in. + * + *

If w == 0, it will be taken to equal + * Math.round(h*(getWidth()/getHeight())). + * Similarly, if h == 0, it will be taken to equal + * Math.round(w*(getHeight()/getWidth())). One of + * w or h must be non-zero or else an IllegalArgumentException + * will be thrown. + * + *

The created RenderedImage may have a property identified + * by the String HINTS_OBSERVED to indicate which RenderingHints + * were used to create the image. In addition any RenderedImages + * that are obtained via the getSources() method on the created + * RenderedImage may have such a property. + * + * @param w the width of rendered image in pixels, or 0. + * @param h the height of rendered image in pixels, or 0. + * @param hints a RenderingHints object containg hints. + * @return a RenderedImage containing the rendered data. + */ + public RenderedImage createScaledRendering(int w, int h, + RenderingHints hints) { + // DSR -- code to try to get a unit scale + double sx = (double)w/getWidth(); + double sy = (double)h/getHeight(); + if (Math.abs(sx/sy - 1.0) < 0.01) { + sx = sy; + } + AffineTransform usr2dev = AffineTransform.getScaleInstance(sx, sy); + RenderContext newRC = new RenderContext(usr2dev, hints); + return createRendering(newRC); + } + + /** + * Gets a RenderedImage instance of this image with a default + * width and height in pixels. The RenderContext is built + * automatically with an appropriate usr2dev transform and an area + * of interest of the full image. All the rendering hints come + * from hints passed in. Implementors of this interface must be + * sure that there is a defined default width and height. + * + * @return a RenderedImage containing the rendered data. + */ + public RenderedImage createDefaultRendering() { + AffineTransform usr2dev = new AffineTransform(); // Identity + RenderContext newRC = new RenderContext(usr2dev); + return createRendering(newRC); + } + + /** + * Creates a RenderedImage which represents this + * RenderableImageOp (including its Renderable sources) rendered + * according to the given RenderContext. + * + *

This method supports chaining of either Renderable or + * RenderedImage operations. If sources in + * the ParameterBlock used to construct the RenderableImageOp are + * RenderableImages, then a three step process is followed: + * + *

    + *
  1. mapRenderContext() is called on the associated CRIF for + * each RenderableImage source; + *
  2. createRendering() is called on each of the RenderableImage sources + * using the backwards-mapped RenderContexts obtained in step 1, + * resulting in a rendering of each source; + *
  3. ContextualRenderedImageFactory.create() is called + * with a new ParameterBlock containing the parameters of + * the RenderableImageOp and the RenderedImages that were created by the + * createRendering() calls. + *
+ * + *

If the elements of the source Vector of + * the ParameterBlock used to construct the RenderableImageOp are + * instances of RenderedImage, then the CRIF.create() method is + * called immediately using the original ParameterBlock. + * This provides a basis case for the recursion. + * + *

The created RenderedImage may have a property identified + * by the String HINTS_OBSERVED to indicate which RenderingHints + * (from the RenderContext) were used to create the image. + * In addition any RenderedImages + * that are obtained via the getSources() method on the created + * RenderedImage may have such a property. + * + * @param renderContext The RenderContext to use to perform the rendering. + * @return a RenderedImage containing the desired output image. + */ + public RenderedImage createRendering(RenderContext renderContext) { + RenderedImage image = null; + RenderContext rcOut = null; + + // Clone the original ParameterBlock; if the ParameterBlock + // contains RenderableImage sources, they will be replaced by + // RenderedImages. + ParameterBlock renderedParamBlock = (ParameterBlock)paramBlock.clone(); + Vector sources = getRenderableSources(); + + try { + // This assumes that if there is no renderable source, that there + // is a rendered source in paramBlock + + if (sources != null) { + Vector renderedSources = new Vector(); + for (int i = 0; i < sources.size(); i++) { + rcOut = myCRIF.mapRenderContext(i, renderContext, + paramBlock, this); + RenderedImage rdrdImage = + ((RenderableImage)sources.elementAt(i)).createRendering(rcOut); + if (rdrdImage == null) { + return null; + } + + // Add this rendered image to the ParameterBlock's + // list of RenderedImages. + renderedSources.addElement(rdrdImage); + } + + if (renderedSources.size() > 0) { + renderedParamBlock.setSources(renderedSources); + } + } + + return myCRIF.create(renderContext, renderedParamBlock); + } catch (ArrayIndexOutOfBoundsException e) { + // This should never happen + return null; + } + } +} diff --git a/jdk/src/share/classes/java/awt/image/renderable/RenderableImageProducer.java b/jdk/src/share/classes/java/awt/image/renderable/RenderableImageProducer.java new file mode 100644 index 00000000000..78f20ce4558 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/renderable/RenderableImageProducer.java @@ -0,0 +1,219 @@ +/* + * Portions Copyright 1998 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* ******************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.image.renderable; +import java.awt.color.ColorSpace; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.util.Enumeration; +import java.util.Vector; + +/** + * An adapter class that implements ImageProducer to allow the + * asynchronous production of a RenderableImage. The size of the + * ImageConsumer is determined by the scale factor of the usr2dev + * transform in the RenderContext. If the RenderContext is null, the + * default rendering of the RenderableImage is used. This class + * implements an asynchronous production that produces the image in + * one thread at one resolution. This class may be subclassed to + * implement versions that will render the image using several + * threads. These threads could render either the same image at + * progressively better quality, or different sections of the image at + * a single resolution. + */ +public class RenderableImageProducer implements ImageProducer, Runnable { + + /** The RenderableImage source for the producer. */ + RenderableImage rdblImage; + + /** The RenderContext to use for producing the image. */ + RenderContext rc; + + /** A Vector of image consumers. */ + Vector ics = new Vector(); + + /** + * Constructs a new RenderableImageProducer from a RenderableImage + * and a RenderContext. + * + * @param rdblImage the RenderableImage to be rendered. + * @param rc the RenderContext to use for producing the pixels. + */ + public RenderableImageProducer(RenderableImage rdblImage, + RenderContext rc) { + this.rdblImage = rdblImage; + this.rc = rc; + } + + /** + * Sets a new RenderContext to use for the next startProduction() call. + * + * @param rc the new RenderContext. + */ + public synchronized void setRenderContext(RenderContext rc) { + this.rc = rc; + } + + /** + * Adds an ImageConsumer to the list of consumers interested in + * data for this image. + * + * @param ic an ImageConsumer to be added to the interest list. + */ + public synchronized void addConsumer(ImageConsumer ic) { + if (!ics.contains(ic)) { + ics.addElement(ic); + } + } + + /** + * Determine if an ImageConsumer is on the list of consumers + * currently interested in data for this image. + * + * @param ic the ImageConsumer to be checked. + * @return true if the ImageConsumer is on the list; false otherwise. + */ + public synchronized boolean isConsumer(ImageConsumer ic) { + return ics.contains(ic); + } + + /** + * Remove an ImageConsumer from the list of consumers interested in + * data for this image. + * + * @param ic the ImageConsumer to be removed. + */ + public synchronized void removeConsumer(ImageConsumer ic) { + ics.removeElement(ic); + } + + /** + * Adds an ImageConsumer to the list of consumers interested in + * data for this image, and immediately starts delivery of the + * image data through the ImageConsumer interface. + * + * @param ic the ImageConsumer to be added to the list of consumers. + */ + public synchronized void startProduction(ImageConsumer ic) { + addConsumer(ic); + // Need to build a runnable object for the Thread. + Thread thread = new Thread(this, "RenderableImageProducer Thread"); + thread.start(); + } + + /** + * Requests that a given ImageConsumer have the image data delivered + * one more time in top-down, left-right order. + * + * @param ic the ImageConsumer requesting the resend. + */ + public void requestTopDownLeftRightResend(ImageConsumer ic) { + // So far, all pixels are already sent in TDLR order + } + + /** + * The runnable method for this class. This will produce an image using + * the current RenderableImage and RenderContext and send it to all the + * ImageConsumer currently registered with this class. + */ + public void run() { + // First get the rendered image + RenderedImage rdrdImage; + if (rc != null) { + rdrdImage = rdblImage.createRendering(rc); + } else { + rdrdImage = rdblImage.createDefaultRendering(); + } + + // And its ColorModel + ColorModel colorModel = rdrdImage.getColorModel(); + Raster raster = rdrdImage.getData(); + SampleModel sampleModel = raster.getSampleModel(); + DataBuffer dataBuffer = raster.getDataBuffer(); + + if (colorModel == null) { + colorModel = ColorModel.getRGBdefault(); + } + int minX = raster.getMinX(); + int minY = raster.getMinY(); + int width = raster.getWidth(); + int height = raster.getHeight(); + + Enumeration icList; + ImageConsumer ic; + // Set up the ImageConsumers + icList = ics.elements(); + while (icList.hasMoreElements()) { + ic = (ImageConsumer)icList.nextElement(); + ic.setDimensions(width,height); + ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT | + ImageConsumer.COMPLETESCANLINES | + ImageConsumer.SINGLEPASS | + ImageConsumer.SINGLEFRAME); + } + + // Get RGB pixels from the raster scanline by scanline and + // send to consumers. + int pix[] = new int[width]; + int i,j; + int numBands = sampleModel.getNumBands(); + int tmpPixel[] = new int[numBands]; + for (j = 0; j < height; j++) { + for(i = 0; i < width; i++) { + sampleModel.getPixel(i, j, tmpPixel, dataBuffer); + pix[i] = colorModel.getDataElement(tmpPixel, 0); + } + // Now send the scanline to the Consumers + icList = ics.elements(); + while (icList.hasMoreElements()) { + ic = (ImageConsumer)icList.nextElement(); + ic.setPixels(0, j, width, 1, colorModel, pix, 0, width); + } + } + + // Now tell the consumers we're done. + icList = ics.elements(); + while (icList.hasMoreElements()) { + ic = (ImageConsumer)icList.nextElement(); + ic.imageComplete(ImageConsumer.STATICIMAGEDONE); + } + } +} diff --git a/jdk/src/share/classes/java/awt/image/renderable/RenderedImageFactory.java b/jdk/src/share/classes/java/awt/image/renderable/RenderedImageFactory.java new file mode 100644 index 00000000000..c04ffbb5914 --- /dev/null +++ b/jdk/src/share/classes/java/awt/image/renderable/RenderedImageFactory.java @@ -0,0 +1,78 @@ +/* + * Portions Copyright 1998 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* ******************************************************************** + ********************************************************************** + ********************************************************************** + *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** + *** As an unpublished work pursuant to Title 17 of the United *** + *** States Code. All rights reserved. *** + ********************************************************************** + ********************************************************************** + **********************************************************************/ + +package java.awt.image.renderable; +import java.awt.image.RenderedImage; +import java.awt.RenderingHints; + +/** + * The RenderedImageFactory interface (often abbreviated RIF) is + * intended to be implemented by classes that wish to act as factories + * to produce different renderings, for example by executing a series + * of BufferedImageOps on a set of sources, depending on a specific + * set of parameters, properties, and rendering hints. + */ +public interface RenderedImageFactory { + + /** + * Creates a RenderedImage representing the results of an imaging + * operation (or chain of operations) for a given ParameterBlock and + * RenderingHints. The RIF may also query any source images + * referenced by the ParameterBlock for their dimensions, + * SampleModels, properties, etc., as necessary. + * + *

The create() method can return null if the + * RenderedImageFactory is not capable of producing output for the + * given set of source images and parameters. For example, if a + * RenderedImageFactory is only capable of performing a 3x3 + * convolution on single-banded image data, and the source image has + * multiple bands or the convolution Kernel is 5x5, null should be + * returned. + * + *

Hints should be taken into account, but can be ignored. + * The created RenderedImage may have a property identified + * by the String HINTS_OBSERVED to indicate which RenderingHints + * were used to create the image. In addition any RenderedImages + * that are obtained via the getSources() method on the created + * RenderedImage may have such a property. + * + * @param paramBlock a ParameterBlock containing sources and parameters + * for the RenderedImage to be created. + * @param hints a RenderingHints object containing hints. + * @return A RenderedImage containing the desired output. + */ + RenderedImage create(ParameterBlock paramBlock, + RenderingHints hints); +} From f82f310abaae448617358e6e4d10694810017085 Mon Sep 17 00:00:00 2001 From: Andrew Haley Date: Thu, 24 Apr 2008 11:58:13 -0700 Subject: [PATCH 08/40] 6523403: Need to provide lcms library with PYCC and LINEAR_RGB OS ICC profiles Add two contributed profiles and a fix to GRAY.pf, all from Redhat, keiths@redhat.com contributed the GRAY.pf fix. Reviewed-by: jgodinez, avu, prr --- jdk/make/sun/cmm/Makefile | 8 ++------ jdk/src/share/lib/cmm/lcms/GRAY.pf | Bin 554 -> 556 bytes jdk/src/share/lib/cmm/lcms/LINEAR_RGB.pf | Bin 0 -> 488 bytes jdk/src/share/lib/cmm/lcms/PYCC.pf | Bin 0 -> 234080 bytes .../java2d/cmm/ProfileOp/ReadProfileTest.java | 2 +- 5 files changed, 3 insertions(+), 7 deletions(-) create mode 100644 jdk/src/share/lib/cmm/lcms/LINEAR_RGB.pf create mode 100644 jdk/src/share/lib/cmm/lcms/PYCC.pf diff --git a/jdk/make/sun/cmm/Makefile b/jdk/make/sun/cmm/Makefile index 66513e5f64b..6aae4d1e326 100644 --- a/jdk/make/sun/cmm/Makefile +++ b/jdk/make/sun/cmm/Makefile @@ -41,12 +41,8 @@ endif # OPENJDK ICCPROFILE_DEST_DIR = $(LIBDIR)/cmm iccprofiles: $(ICCPROFILE_DEST_DIR)/sRGB.pf $(ICCPROFILE_DEST_DIR)/GRAY.pf \ - $(ICCPROFILE_DEST_DIR)/CIEXYZ.pf - -ifndef OPENJDK -iccprofiles: $(ICCPROFILE_DEST_DIR)/PYCC.pf \ - $(ICCPROFILE_DEST_DIR)/LINEAR_RGB.pf -endif + $(ICCPROFILE_DEST_DIR)/CIEXYZ.pf $(ICCPROFILE_DEST_DIR)/PYCC.pf \ + $(ICCPROFILE_DEST_DIR)/LINEAR_RGB.pf $(ICCPROFILE_DEST_DIR)/sRGB.pf: $(ICCPROFILE_SRC_DIR)/sRGB.pf $(RM) $(ICCPROFILE_DEST_DIR)/sRGB.pf diff --git a/jdk/src/share/lib/cmm/lcms/GRAY.pf b/jdk/src/share/lib/cmm/lcms/GRAY.pf index 3686df40d2491ab448d6b0e5c71bf91dd9900f26..8af309cc0a2cc44d07b5bbab06c8187c6f18dc7d 100644 GIT binary patch delta 16 XcmZ3*vWA6)fq_Y9Ba0dn69WSP8|4CU delta 13 UcmZ3(vWkU;fq_YDBa0dn02Ge`YybcN diff --git a/jdk/src/share/lib/cmm/lcms/LINEAR_RGB.pf b/jdk/src/share/lib/cmm/lcms/LINEAR_RGB.pf new file mode 100644 index 0000000000000000000000000000000000000000..3e0b1777a8c6ba9081c07fc45eb13058f0dde9fe GIT binary patch literal 488 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 C%~6Q} literal 0 HcmV?d00001 diff --git a/jdk/test/sun/java2d/cmm/ProfileOp/ReadProfileTest.java b/jdk/test/sun/java2d/cmm/ProfileOp/ReadProfileTest.java index 42002f96b45..1a4570f9c6d 100644 --- a/jdk/test/sun/java2d/cmm/ProfileOp/ReadProfileTest.java +++ b/jdk/test/sun/java2d/cmm/ProfileOp/ReadProfileTest.java @@ -23,7 +23,7 @@ /** * @test - * @bug 6476665 + * @bug 6476665 6523403 * @summary Verifies reading profiles of the standard color spaces * @run main ReadProfileTest */ From 9e39cf49ed3928700265cfad8cd82f35c7d56bb0 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 25 Apr 2008 10:37:07 -0700 Subject: [PATCH 09/40] 6687298: Reg testcase java/awt/Graphics2D/DrawString/RotTransText.java fails on windows Reviewed-by: igor, tdv --- .../Graphics2D/DrawString/RotTransText.java | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java b/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java index e130b1c4ae4..9f38fa113f2 100644 --- a/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java +++ b/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java @@ -23,7 +23,7 @@ /** * @test - * @bug 6683472 + * @bug 6683472 6687298 * @summary Transformed fonts using drawString and TextLayout should be in * the same position. */ @@ -44,14 +44,15 @@ public class RotTransText { Graphics2D g2d = bi.createGraphics(); - g2d.setColor(Color.white); - g2d.fillRect(0, 0, wid, hgt); - int x=130, y=130; String s = "Text"; int xt=90, yt=50; for (int angle=0;angle<360;angle+=30) { + + g2d.setColor(Color.white); + g2d.fillRect(0, 0, wid, hgt); + AffineTransform aff = AffineTransform.getTranslateInstance(50,90); aff.rotate(angle * Math.PI/180.0); @@ -69,20 +70,27 @@ public class RotTransText { fnt = fnt.deriveFont(attrMap); TextLayout tl = new TextLayout(s, fnt, frc); tl.draw(g2d, (float)x, (float)y); - } - // Test BI: should be no blue: only red and white. - int red = Color.red.getRGB(); - int blue = Color.blue.getRGB(); - int white = Color.white.getRGB(); - for (int px=0;px Date: Fri, 25 Apr 2008 10:40:11 -0700 Subject: [PATCH 10/40] 6692979: VM Crash when shearing text + rect over a range of values Reviewed-by: igor, tdv --- .../classes/sun/font/FileFontStrike.java | 7 +- jdk/test/java/awt/font/Rotate/Shear.java | 66 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 jdk/test/java/awt/font/Rotate/Shear.java diff --git a/jdk/src/share/classes/sun/font/FileFontStrike.java b/jdk/src/share/classes/sun/font/FileFontStrike.java index edc9e67b6cb..79890d400ec 100644 --- a/jdk/src/share/classes/sun/font/FileFontStrike.java +++ b/jdk/src/share/classes/sun/font/FileFontStrike.java @@ -217,7 +217,12 @@ public class FileFontStrike extends PhysicalStrike { * "maximumSizeForGetImageWithAdvance". * This should be no greater than OutlineTextRender.THRESHOLD. */ - getImageWithAdvance = at.getScaleY() <= 48.0; + double maxSz = 48.0; + getImageWithAdvance = + Math.abs(at.getScaleX()) <= maxSz && + Math.abs(at.getScaleY()) <= maxSz && + Math.abs(at.getShearX()) <= maxSz && + Math.abs(at.getShearY()) <= maxSz; /* Some applications request advance frequently during layout. * If we are not getting and caching the image with the advance, diff --git a/jdk/test/java/awt/font/Rotate/Shear.java b/jdk/test/java/awt/font/Rotate/Shear.java new file mode 100644 index 00000000000..5b109b40d5d --- /dev/null +++ b/jdk/test/java/awt/font/Rotate/Shear.java @@ -0,0 +1,66 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6692979 + * @summary Verify no crashes with extreme shears. + */ + +import javax.swing.*; +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +public class Shear extends Component { + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.getContentPane().add("Center", new Shear()); + f.pack(); + f.setVisible(true); + } + + public Dimension getPreferredSize() { + return new Dimension(400,300); + } + + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D)g; + + g.setColor(Color.white); + g.fillRect(0,0,400,300); + g.setColor(Color.black); + Font origFont = new Font(Font.DIALOG, Font.BOLD, 30); + for (int i=0;i<=360;i++) { + double sv = i*180.0/Math.PI; + AffineTransform tx = AffineTransform.getShearInstance(sv, sv); + Font font = origFont.deriveFont(tx); + g.setFont(font); + GlyphVector gv = + font.createGlyphVector(g2.getFontRenderContext(), "JavaFX"); + //System.out.println(gv.getVisualBounds()); + g.drawString("JavaFX", 100, 100); + } + } +} From 88cfc253d10bf7f0267e3c17095b462f2e8b15ec Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 28 Apr 2008 09:59:35 -0700 Subject: [PATCH 11/40] 6694480: Two small inefficiencies in getting font strikes for transformed fonts Reviewed-by: igor, tdv --- jdk/src/share/classes/java/awt/Font.java | 3 +-- jdk/src/share/classes/sun/font/Font2D.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/java/awt/Font.java b/jdk/src/share/classes/java/awt/Font.java index ba2cf3e3976..8dc5938d411 100644 --- a/jdk/src/share/classes/java/awt/Font.java +++ b/jdk/src/share/classes/java/awt/Font.java @@ -1970,7 +1970,6 @@ public class Font implements java.io.Serializable * in the JDK - and the only likely caller - is in this same class. */ private float getItalicAngle(FontRenderContext frc) { - AffineTransform at = (isTransformed()) ? getTransform() : identityTx; Object aa, fm; if (frc == null) { aa = RenderingHints.VALUE_TEXT_ANTIALIAS_OFF; @@ -1979,7 +1978,7 @@ public class Font implements java.io.Serializable aa = frc.getAntiAliasingHint(); fm = frc.getFractionalMetricsHint(); } - return getFont2D().getItalicAngle(this, at, aa, fm); + return getFont2D().getItalicAngle(this, identityTx, aa, fm); } /** diff --git a/jdk/src/share/classes/sun/font/Font2D.java b/jdk/src/share/classes/sun/font/Font2D.java index 0bf23547ad0..bfcbc0329d4 100644 --- a/jdk/src/share/classes/sun/font/Font2D.java +++ b/jdk/src/share/classes/sun/font/Font2D.java @@ -241,6 +241,13 @@ public abstract class Font2D { if (font.isTransformed()) { glyphTx.concatenate(font.getTransform()); } + if (glyphTx.getTranslateX() != 0 || glyphTx.getTranslateY() != 0) { + glyphTx.setTransform(glyphTx.getScaleX(), + glyphTx.getShearY(), + glyphTx.getShearX(), + glyphTx.getScaleY(), + 0.0, 0.0); + } FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx, font.getStyle(), aa, fm); return getStrike(desc, false); @@ -266,6 +273,13 @@ public abstract class Font2D { at.scale(ptSize, ptSize); if (font.isTransformed()) { at.concatenate(font.getTransform()); + if (at.getTranslateX() != 0 || at.getTranslateY() != 0) { + at.setTransform(at.getScaleX(), + at.getShearY(), + at.getShearX(), + at.getScaleY(), + 0.0, 0.0); + } } int aa = FontStrikeDesc.getAAHintIntVal(this, font, frc); int fm = FontStrikeDesc.getFMHintIntVal(frc.getFractionalMetricsHint()); From cb5a8e47fb141d768440a11f6aaaa94cc1a17f48 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 28 Apr 2008 11:06:18 -0700 Subject: [PATCH 12/40] 6664915: SecurityException using javax.print APIs when queuePrintJob permission is granted Reviewed-by: tdv, jgodinez --- .../sun/awt/Win32GraphicsEnvironment.java | 26 ++++++---- jdk/test/javax/print/PrintSE/PrintSE.java | 52 +++++++++++++++++++ jdk/test/javax/print/PrintSE/PrintSE.sh | 51 ++++++++++++++++++ 3 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 jdk/test/javax/print/PrintSE/PrintSE.java create mode 100644 jdk/test/javax/print/PrintSE/PrintSE.sh diff --git a/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java b/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java index a52b37cca61..89222518533 100644 --- a/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java +++ b/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java @@ -296,7 +296,7 @@ public class Win32GraphicsEnvironment } public static void registerJREFontsForPrinting() { - String pathName = null; + final String pathName; synchronized (Win32GraphicsEnvironment.class) { GraphicsEnvironment.getLocalGraphicsEnvironment(); if (fontsForPrinting == null) { @@ -305,15 +305,21 @@ public class Win32GraphicsEnvironment pathName = fontsForPrinting; fontsForPrinting = null; } - File f1 = new File(pathName); - String[] ls = f1.list(new TTFilter()); - if (ls == null) { - return; - } - for (int i=0; i 0) return NO_SUCH_PAGE; + g.drawString("Test passes.", 100, 100); + return PAGE_EXISTS; + } +} diff --git a/jdk/test/javax/print/PrintSE/PrintSE.sh b/jdk/test/javax/print/PrintSE/PrintSE.sh new file mode 100644 index 00000000000..95cbbc4c121 --- /dev/null +++ b/jdk/test/javax/print/PrintSE/PrintSE.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +# +# Copyright 2008 Sun Microsystems, Inc. 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. +# +# 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# @test +# @bug 6662775 +# @summary Tests queuePrintJob is sufficient permission. +# @run clean PrintSE +# @run build PrintSE +# @run compile PrintSE.java +# @run shell/timeout=300 PrintSE.sh + +echo ------------------------------------------------------------- +echo Launching test for `basename $0 .sh` +echo ------------------------------------------------------------- + +createJavaPolicyFile() +{ + cat << EOF > ${TESTCLASSES}/print.policy +grant { + permission java.lang.RuntimePermission "queuePrintJob"; +}; +EOF +} + +createJavaPolicyFile + +${TESTJAVA}/bin/java -Djava.security.manager -Djava.security.policy=${TESTCLASSES}/print.policy -cp ${TESTCLASSES} PrintSE + +exit $? From c672506f950461389bd2ecfd887650086d77e16d Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 28 Apr 2008 15:57:46 -0700 Subject: [PATCH 13/40] 6679308: Poor text rendering on translucent image Reviewed-by: flar, campbell --- .../native/sun/java2d/loops/AlphaMacros.h | 12 +- .../share/native/sun/java2d/loops/ByteGray.h | 2 + .../native/sun/java2d/loops/FourByteAbgr.h | 2 + .../native/sun/java2d/loops/FourByteAbgrPre.h | 2 + .../native/sun/java2d/loops/Index12Gray.h | 2 + .../native/sun/java2d/loops/Index8Gray.h | 2 + .../share/native/sun/java2d/loops/IntArgb.h | 2 + .../share/native/sun/java2d/loops/IntArgbBm.h | 2 + .../native/sun/java2d/loops/IntArgbPre.h | 2 + .../share/native/sun/java2d/loops/IntBgr.h | 2 + .../share/native/sun/java2d/loops/IntRgb.h | 2 + .../share/native/sun/java2d/loops/IntRgbx.h | 2 + .../native/sun/java2d/loops/LoopMacros.h | 16 ++- .../native/sun/java2d/loops/ThreeByteBgr.h | 2 + .../native/sun/java2d/loops/Ushort4444Argb.h | 2 + .../native/sun/java2d/loops/Ushort555Rgb.h | 2 + .../native/sun/java2d/loops/Ushort555Rgbx.h | 2 + .../native/sun/java2d/loops/Ushort565Rgb.h | 2 + .../native/sun/java2d/loops/UshortGray.h | 2 + .../sun/java2d/loops/vis_FourByteAbgr.c | 14 ++- .../sun/java2d/loops/vis_FourByteAbgrPre.c | 32 +++++- .../native/sun/java2d/loops/vis_IntArgb.c | 8 ++ .../native/sun/java2d/loops/vis_IntArgbPre.c | 4 - .../DrawString/AlphaSurfaceText.java | 106 ++++++++++++++++++ 24 files changed, 209 insertions(+), 17 deletions(-) create mode 100644 jdk/test/java/awt/Graphics2D/DrawString/AlphaSurfaceText.java diff --git a/jdk/src/share/native/sun/java2d/loops/AlphaMacros.h b/jdk/src/share/native/sun/java2d/loops/AlphaMacros.h index a7c0c84c38a..3053003f455 100644 --- a/jdk/src/share/native/sun/java2d/loops/AlphaMacros.h +++ b/jdk/src/share/native/sun/java2d/loops/AlphaMacros.h @@ -416,7 +416,8 @@ void NAME_SRCOVER_MASKBLIT(SRC, DST) \ MultiplyAndStore ## STRATEGY ## Comps(res, \ srcF, res);\ } \ - if (!(DST ## IsPremultiplied) && resA && \ + if (!(DST ## IsOpaque) && \ + !(DST ## IsPremultiplied) && resA && \ resA < MaxValFor ## STRATEGY) \ { \ DivideAndStore ## STRATEGY ## Comps(res, \ @@ -475,7 +476,8 @@ void NAME_SRCOVER_MASKBLIT(SRC, DST) \ MultiplyAndStore ## STRATEGY ## Comps(res, \ srcF, res); \ } \ - if (!(DST ## IsPremultiplied) && resA && \ + if (!(DST ## IsOpaque) && \ + !(DST ## IsPremultiplied) && resA && \ resA < MaxValFor ## STRATEGY) \ { \ DivideAndStore ## STRATEGY ## Comps(res, res, resA); \ @@ -797,7 +799,8 @@ void NAME_SRCOVER_MASKFILL(TYPE) \ Store ## STRATEGY ## CompsUsingOp(res, +=, tmp); \ } \ } \ - if (!(TYPE ## IsPremultiplied) && resA && \ + if (!(TYPE ## IsOpaque) && \ + !(TYPE ## IsPremultiplied) && resA && \ resA < MaxValFor ## STRATEGY) \ { \ DivideAndStore ## STRATEGY ## Comps(res, res, resA); \ @@ -831,7 +834,8 @@ void NAME_SRCOVER_MASKFILL(TYPE) \ Postload ## STRATEGY ## From ## TYPE(pRas, DstPix, res); \ MultiplyAddAndStore ## STRATEGY ## Comps(res, \ dstF, res, src); \ - if (!(TYPE ## IsPremultiplied) && resA && \ + if (!(TYPE ## IsOpaque) && \ + !(TYPE ## IsPremultiplied) && resA && \ resA < MaxValFor ## STRATEGY) \ { \ DivideAndStore ## STRATEGY ## Comps(res, res, resA); \ diff --git a/jdk/src/share/native/sun/java2d/loops/ByteGray.h b/jdk/src/share/native/sun/java2d/loops/ByteGray.h index 335a47b7a89..5cb4bd264cb 100644 --- a/jdk/src/share/native/sun/java2d/loops/ByteGray.h +++ b/jdk/src/share/native/sun/java2d/loops/ByteGray.h @@ -36,6 +36,8 @@ typedef jubyte ByteGrayPixelType; typedef jubyte ByteGrayDataType; +#define ByteGrayIsOpaque 1 + #define ByteGrayPixelStride 1 #define ByteGrayBitsPerPixel 8 diff --git a/jdk/src/share/native/sun/java2d/loops/FourByteAbgr.h b/jdk/src/share/native/sun/java2d/loops/FourByteAbgr.h index 2af1150f7e7..5f77047d6ad 100644 --- a/jdk/src/share/native/sun/java2d/loops/FourByteAbgr.h +++ b/jdk/src/share/native/sun/java2d/loops/FourByteAbgr.h @@ -34,6 +34,8 @@ typedef jint FourByteAbgrPixelType; typedef jubyte FourByteAbgrDataType; +#define FourByteAbgrIsOpaque 0 + #define FourByteAbgrPixelStride 4 #define DeclareFourByteAbgrLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/FourByteAbgrPre.h b/jdk/src/share/native/sun/java2d/loops/FourByteAbgrPre.h index 8b4b9f7e59a..c6bde946e57 100644 --- a/jdk/src/share/native/sun/java2d/loops/FourByteAbgrPre.h +++ b/jdk/src/share/native/sun/java2d/loops/FourByteAbgrPre.h @@ -34,6 +34,8 @@ typedef jint FourByteAbgrPrePixelType; typedef jubyte FourByteAbgrPreDataType; +#define FourByteAbgrPreIsOpaque 0 + #define FourByteAbgrPrePixelStride 4 #define DeclareFourByteAbgrPreLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/Index12Gray.h b/jdk/src/share/native/sun/java2d/loops/Index12Gray.h index bb583b6ce2b..f728c14ff5b 100644 --- a/jdk/src/share/native/sun/java2d/loops/Index12Gray.h +++ b/jdk/src/share/native/sun/java2d/loops/Index12Gray.h @@ -37,6 +37,8 @@ typedef jushort Index12GrayPixelType; typedef jushort Index12GrayDataType; +#define Index12GrayIsOpaque 1 + #define Index12GrayPixelStride 2 #define Index12GrayBitsPerPixel 12 diff --git a/jdk/src/share/native/sun/java2d/loops/Index8Gray.h b/jdk/src/share/native/sun/java2d/loops/Index8Gray.h index 15e2ecdc799..2691863af81 100644 --- a/jdk/src/share/native/sun/java2d/loops/Index8Gray.h +++ b/jdk/src/share/native/sun/java2d/loops/Index8Gray.h @@ -37,6 +37,8 @@ typedef jubyte Index8GrayPixelType; typedef jubyte Index8GrayDataType; +#define Index8GrayIsOpaque 1 + #define Index8GrayPixelStride 1 #define Index8GrayBitsPerPixel 8 diff --git a/jdk/src/share/native/sun/java2d/loops/IntArgb.h b/jdk/src/share/native/sun/java2d/loops/IntArgb.h index 26aa69a9369..cdc8435ced0 100644 --- a/jdk/src/share/native/sun/java2d/loops/IntArgb.h +++ b/jdk/src/share/native/sun/java2d/loops/IntArgb.h @@ -38,6 +38,8 @@ typedef jint IntArgbPixelType; typedef jint IntArgbDataType; +#define IntArgbIsOpaque 0 + #define IntArgbPixelStride 4 #define DeclareIntArgbLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/IntArgbBm.h b/jdk/src/share/native/sun/java2d/loops/IntArgbBm.h index 4a07a98a388..201c2ff92b8 100644 --- a/jdk/src/share/native/sun/java2d/loops/IntArgbBm.h +++ b/jdk/src/share/native/sun/java2d/loops/IntArgbBm.h @@ -38,6 +38,8 @@ typedef jint IntArgbBmPixelType; typedef jint IntArgbBmDataType; +#define IntArgbBmIsOpaque 0 + #define IntArgbBmPixelStride 4 #define DeclareIntArgbBmLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/IntArgbPre.h b/jdk/src/share/native/sun/java2d/loops/IntArgbPre.h index 9657af26f7f..14f4b809042 100644 --- a/jdk/src/share/native/sun/java2d/loops/IntArgbPre.h +++ b/jdk/src/share/native/sun/java2d/loops/IntArgbPre.h @@ -36,6 +36,8 @@ typedef jint IntArgbPrePixelType; typedef jint IntArgbPreDataType; +#define IntArgbPreIsOpaque 0 + #define IntArgbPrePixelStride 4 #define DeclareIntArgbPreLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/IntBgr.h b/jdk/src/share/native/sun/java2d/loops/IntBgr.h index f2f99eba0f9..124ee70b7b3 100644 --- a/jdk/src/share/native/sun/java2d/loops/IntBgr.h +++ b/jdk/src/share/native/sun/java2d/loops/IntBgr.h @@ -38,6 +38,8 @@ typedef jint IntBgrPixelType; typedef jint IntBgrDataType; +#define IntBgrIsOpaque 1 + #define IntBgrPixelStride 4 #define DeclareIntBgrLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/IntRgb.h b/jdk/src/share/native/sun/java2d/loops/IntRgb.h index 9fc4d076437..1f74bfe256c 100644 --- a/jdk/src/share/native/sun/java2d/loops/IntRgb.h +++ b/jdk/src/share/native/sun/java2d/loops/IntRgb.h @@ -38,6 +38,8 @@ typedef jint IntRgbPixelType; typedef jint IntRgbDataType; +#define IntRgbIsOpaque 1 + #define IntRgbPixelStride 4 #define DeclareIntRgbLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/IntRgbx.h b/jdk/src/share/native/sun/java2d/loops/IntRgbx.h index e695ac846ee..6e774c4f5fd 100644 --- a/jdk/src/share/native/sun/java2d/loops/IntRgbx.h +++ b/jdk/src/share/native/sun/java2d/loops/IntRgbx.h @@ -36,6 +36,8 @@ typedef jint IntRgbxPixelType; typedef jint IntRgbxDataType; +#define IntRgbxIsOpaque 1 + #define IntRgbxPixelStride 4 #define DeclareIntRgbxLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/LoopMacros.h b/jdk/src/share/native/sun/java2d/loops/LoopMacros.h index 45199c81ecb..f009404648c 100644 --- a/jdk/src/share/native/sun/java2d/loops/LoopMacros.h +++ b/jdk/src/share/native/sun/java2d/loops/LoopMacros.h @@ -1610,8 +1610,12 @@ void NAME_SOLID_DRAWGLYPHLIST(DST)(SurfaceDataRasInfo *pRasInfo, \ MUL8(SRC_PREFIX ## A, mixValSrc); \ MultMultAddAndStore4ByteArgbComps(dst, mixValDst, dst, \ mixValSrc, SRC_PREFIX); \ - Store ## DST ## From4ByteArgb(DST_PTR, pix, PIXEL_INDEX, \ - dstA, dstR, dstG, dstB); \ + if (!(DST ## IsOpaque) && \ + !(DST ## IsPremultiplied) && dstA && dstA < 255) { \ + DivideAndStore4ByteArgbComps(dst, dst, dstA); \ + } \ + Store ## DST ## From4ByteArgbComps(DST_PTR, pix, \ + PIXEL_INDEX, dst); \ } else { \ Store ## DST ## PixelData(DST_PTR, PIXEL_INDEX, \ FG_PIXEL, PREFIX); \ @@ -1793,8 +1797,12 @@ void NAME_SOLID_DRAWGLYPHLISTAA(DST)(SurfaceDataRasInfo *pRasInfo, \ dstR = gammaLut[dstR]; \ dstG = gammaLut[dstG]; \ dstB = gammaLut[dstB]; \ - Store ## DST ## From4ByteArgb(DST_PTR, pix, PIXEL_INDEX, \ - dstA, dstR, dstG, dstB); \ + if (!(DST ## IsOpaque) && \ + !(DST ## IsPremultiplied) && dstA && dstA < 255) { \ + DivideAndStore4ByteArgbComps(dst, dst, dstA); \ + } \ + Store ## DST ## From4ByteArgbComps(DST_PTR, pix, \ + PIXEL_INDEX, dst); \ } else { \ Store ## DST ## PixelData(DST_PTR, PIXEL_INDEX, \ FG_PIXEL, PREFIX); \ diff --git a/jdk/src/share/native/sun/java2d/loops/ThreeByteBgr.h b/jdk/src/share/native/sun/java2d/loops/ThreeByteBgr.h index 7bb4944f520..16641c8bc89 100644 --- a/jdk/src/share/native/sun/java2d/loops/ThreeByteBgr.h +++ b/jdk/src/share/native/sun/java2d/loops/ThreeByteBgr.h @@ -34,6 +34,8 @@ typedef jint ThreeByteBgrPixelType; typedef jubyte ThreeByteBgrDataType; +#define ThreeByteBgrIsOpaque 1 + #define ThreeByteBgrPixelStride 3 #define DeclareThreeByteBgrLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/Ushort4444Argb.h b/jdk/src/share/native/sun/java2d/loops/Ushort4444Argb.h index 216f521b297..3eac497c292 100644 --- a/jdk/src/share/native/sun/java2d/loops/Ushort4444Argb.h +++ b/jdk/src/share/native/sun/java2d/loops/Ushort4444Argb.h @@ -34,6 +34,8 @@ typedef jushort Ushort4444ArgbPixelType; typedef jushort Ushort4444ArgbDataType; +#define Ushort4444ArgbIsOpaque 0 + #define Ushort4444ArgbPixelStride 2 #define DeclareUshort4444ArgbLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/Ushort555Rgb.h b/jdk/src/share/native/sun/java2d/loops/Ushort555Rgb.h index 5c59c46da30..e52516a958f 100644 --- a/jdk/src/share/native/sun/java2d/loops/Ushort555Rgb.h +++ b/jdk/src/share/native/sun/java2d/loops/Ushort555Rgb.h @@ -34,6 +34,8 @@ typedef jushort Ushort555RgbPixelType; typedef jushort Ushort555RgbDataType; +#define Ushort555RgbIsOpaque 1 + #define Ushort555RgbPixelStride 2 #define DeclareUshort555RgbLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/Ushort555Rgbx.h b/jdk/src/share/native/sun/java2d/loops/Ushort555Rgbx.h index 4586ba23829..e517e2feddf 100644 --- a/jdk/src/share/native/sun/java2d/loops/Ushort555Rgbx.h +++ b/jdk/src/share/native/sun/java2d/loops/Ushort555Rgbx.h @@ -34,6 +34,8 @@ typedef jushort Ushort555RgbxPixelType; typedef jushort Ushort555RgbxDataType; +#define Ushort555RgbxIsOpaque 1 + #define Ushort555RgbxPixelStride 2 #define DeclareUshort555RgbxLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/Ushort565Rgb.h b/jdk/src/share/native/sun/java2d/loops/Ushort565Rgb.h index 68008b3a500..2571b9fe442 100644 --- a/jdk/src/share/native/sun/java2d/loops/Ushort565Rgb.h +++ b/jdk/src/share/native/sun/java2d/loops/Ushort565Rgb.h @@ -34,6 +34,8 @@ typedef jushort Ushort565RgbPixelType; typedef jushort Ushort565RgbDataType; +#define Ushort565RgbIsOpaque 1 + #define Ushort565RgbPixelStride 2 #define DeclareUshort565RgbLoadVars(PREFIX) diff --git a/jdk/src/share/native/sun/java2d/loops/UshortGray.h b/jdk/src/share/native/sun/java2d/loops/UshortGray.h index 89c65cf41fa..8370af99809 100644 --- a/jdk/src/share/native/sun/java2d/loops/UshortGray.h +++ b/jdk/src/share/native/sun/java2d/loops/UshortGray.h @@ -36,6 +36,8 @@ typedef jushort UshortGrayPixelType; typedef jushort UshortGrayDataType; +#define UshortGrayIsOpaque 1 + #define UshortGrayPixelStride 2 #define UshortGrayBitsPerPixel 16 diff --git a/jdk/src/solaris/native/sun/java2d/loops/vis_FourByteAbgr.c b/jdk/src/solaris/native/sun/java2d/loops/vis_FourByteAbgr.c index 2745801a181..75ef3363801 100644 --- a/jdk/src/solaris/native/sun/java2d/loops/vis_FourByteAbgr.c +++ b/jdk/src/solaris/native/sun/java2d/loops/vis_FourByteAbgr.c @@ -1936,6 +1936,7 @@ void ADD_SUFF(FourByteAbgrDrawGlyphListAA)(SurfaceDataRasInfo * pRasInfo, for (j = 0; j < height; j++) { mlib_u8 *src = (void*)pixels; mlib_s32 *dst, *dst_end; + mlib_u8 *dst_start; if ((mlib_s32)dstBase & 3) { COPY_NA(dstBase, pbuff, width*sizeof(mlib_s32)); @@ -1943,8 +1944,14 @@ void ADD_SUFF(FourByteAbgrDrawGlyphListAA)(SurfaceDataRasInfo * pRasInfo, } else { dst = (void*)dstBase; } + dst_start = (void*)dst; dst_end = dst + width; + /* Need to reset the GSR from the values set by the + * convert call near the end of this loop. + */ + vis_write_gsr(7 << 0); + if ((mlib_s32)dst & 7) { pix = *src++; dd = vis_fpadd16(MUL8_VIS(srcG_f, pix), d_half); @@ -1984,8 +1991,13 @@ void ADD_SUFF(FourByteAbgrDrawGlyphListAA)(SurfaceDataRasInfo * pRasInfo, dst++; } + ADD_SUFF(IntArgbPreToIntArgbConvert)(dst_start, dst_start, + width, 1, + pRasInfo, pRasInfo, + pPrim, pCompInfo); + if ((mlib_s32)dstBase & 3) { - COPY_NA(pbuff, dstBase, width*sizeof(mlib_s32)); + COPY_NA(dst_start, dstBase, width*sizeof(mlib_s32)); } PTR_ADD(dstBase, scan); diff --git a/jdk/src/solaris/native/sun/java2d/loops/vis_FourByteAbgrPre.c b/jdk/src/solaris/native/sun/java2d/loops/vis_FourByteAbgrPre.c index 6e5074e79ac..aab22666746 100644 --- a/jdk/src/solaris/native/sun/java2d/loops/vis_FourByteAbgrPre.c +++ b/jdk/src/solaris/native/sun/java2d/loops/vis_FourByteAbgrPre.c @@ -181,6 +181,7 @@ void ADD_SUFF(FourByteAbgrPreDrawGlyphListAA)(SurfaceDataRasInfo * pRasInfo, d_half = vis_to_double_dup((1 << (16 + 6)) | (1 << 6)); srcG_f = vis_to_float(argbcolor); + ARGB2ABGR_FL(srcG_f); for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) { const jubyte *pixels; @@ -238,8 +239,33 @@ void ADD_SUFF(FourByteAbgrPreDrawGlyphListAA)(SurfaceDataRasInfo * pRasInfo, mlib_u8 *src = (void*)pixels; mlib_s32 *dst, *dst_end; mlib_u8 *dst8; + mlib_u8* dst_start = dstBase; - ADD_SUFF(FourByteAbgrPreToIntArgbConvert)(dstBase, pbuff, width, 1, + /* + * Typically the inner loop here works on Argb input data, an + * Argb color, and produces ArgbPre output data. To use that + * standard approach we would need a FourByteAbgrPre to IntArgb + * converter for the front end and an IntArgbPre to FourByteAbgrPre + * converter for the back end. The converter exists for the + * front end, but it is a workaround implementation that uses a 2 + * stage conversion and an intermediate buffer that is allocated + * on every call. The converter for the back end doesn't really + * exist, but we could reuse the IntArgb to FourByteAbgr converter + * to do the same work - at the cost of swapping the components as + * we copy the data back. All of this is more work than we really + * need so we use an alternate procedure: + * - Copy the data into an int-aligned temporary buffer (if needed) + * - Convert the data from FourByteAbgrPre to IntAbgr by using the + * IntArgbPre to IntArgb converter in the int-aligned buffer. + * - Swap the color data to Abgr so that the inner loop goes from + * IntAbgr data to IntAbgrPre data + * - Simply copy the IntAbgrPre data back into place. + */ + if (((mlib_s32)dstBase) & 3) { + COPY_NA(dstBase, pbuff, width*sizeof(mlib_s32)); + dst_start = pbuff; + } + ADD_SUFF(IntArgbPreToIntArgbConvert)(dst_start, pbuff, width, 1, pRasInfo, pRasInfo, pPrim, pCompInfo); @@ -283,9 +309,7 @@ void ADD_SUFF(FourByteAbgrPreDrawGlyphListAA)(SurfaceDataRasInfo * pRasInfo, dst++; } - ADD_SUFF(IntArgbToFourByteAbgrPreConvert)(pbuff, dstBase, width, 1, - pRasInfo, pRasInfo, - pPrim, pCompInfo); + COPY_NA(pbuff, dstBase, width*sizeof(mlib_s32)); src = (void*)pixels; dst8 = (void*)dstBase; diff --git a/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgb.c b/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgb.c index 69fc04339c8..72bde3a6b20 100644 --- a/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgb.c +++ b/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgb.c @@ -428,6 +428,11 @@ void ADD_SUFF(IntArgbDrawGlyphListAA)(GLYPH_LIST_PARAMS) dst = (void*)dstBase; dst_end = dst + width; + /* Clearing the Graphics Status Register is necessary otherwise + * left over scale settings affect the pack instructions. + */ + vis_write_gsr(0 << 3); + if ((mlib_s32)dst & 7) { pix = *src++; dd = vis_fpadd16(MUL8_VIS(srcG_f, pix), d_half); @@ -467,6 +472,9 @@ void ADD_SUFF(IntArgbDrawGlyphListAA)(GLYPH_LIST_PARAMS) dst++; } + ADD_SUFF(IntArgbPreToIntArgbConvert)(dstBase, dstBase, width, 1, + pRasInfo, pRasInfo, + pPrim, pCompInfo); PTR_ADD(dstBase, scan); pixels += rowBytes; } diff --git a/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre.c b/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre.c index cd773365ea8..8c4f4f583f6 100644 --- a/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre.c +++ b/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre.c @@ -1193,10 +1193,6 @@ void ADD_SUFF(IntArgbPreDrawGlyphListAA)(SurfaceDataRasInfo * pRasInfo, dst++; } - ADD_SUFF(IntArgbToIntArgbPreConvert)(dstBase, dstBase, width, 1, - pRasInfo, pRasInfo, - pPrim, pCompInfo); - PTR_ADD(dstBase, scan); pixels += rowBytes; } diff --git a/jdk/test/java/awt/Graphics2D/DrawString/AlphaSurfaceText.java b/jdk/test/java/awt/Graphics2D/DrawString/AlphaSurfaceText.java new file mode 100644 index 00000000000..219b5e3565b --- /dev/null +++ b/jdk/test/java/awt/Graphics2D/DrawString/AlphaSurfaceText.java @@ -0,0 +1,106 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 6679308 + * @summary test drawing to Alpha surfaces + */ + +import java.awt.*; +import java.awt.image.*; + +public class AlphaSurfaceText { + + int wid=400, hgt=200; + + public AlphaSurfaceText(int biType, Color c) { + BufferedImage opaquebi0 = + new BufferedImage(wid, hgt, BufferedImage.TYPE_INT_RGB); + drawText(opaquebi0, c); + + BufferedImage alphabi = new BufferedImage(wid, hgt, biType); + drawText(alphabi, c); + BufferedImage opaquebi1 = + new BufferedImage(wid, hgt, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = opaquebi1.createGraphics(); + g2d.drawImage(alphabi, 0, 0, null); + compare(opaquebi0, opaquebi1, biType, c); + } + + private void drawText(BufferedImage bi, Color c) { + Graphics2D g = bi.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setColor(c); + g.setFont(new Font("sansserif", Font.PLAIN, 70)); + g.drawString("Hello!", 20, 100); + g.setFont(new Font("sansserif", Font.PLAIN, 12)); + g.drawString("Hello!", 20, 130); + g.setFont(new Font("sansserif", Font.PLAIN, 10)); + g.drawString("Hello!", 20, 150); + } + + // Need to allow for minimal rounding error, so allow each component + // to differ by 1. + void compare(BufferedImage bi0, BufferedImage bi1, int biType, Color c) { + for (int x=0; x> 16; + int r1 = (rgb1 & 0xff0000) >> 16; + int rdiff = r0-r1; if (rdiff<0) rdiff = -rdiff; + int g0 = (rgb0 & 0x00ff00) >> 8; + int g1 = (rgb1 & 0x00ff00) >> 8; + int gdiff = g0-g1; if (gdiff<0) gdiff = -gdiff; + int b0 = (rgb0 & 0x0000ff); + int b1 = (rgb1 & 0x0000ff); + int bdiff = b0-b1; if (bdiff<0) bdiff = -bdiff; + if (rdiff > 1 || gdiff > 1 || bdiff > 1) { + throw new RuntimeException( + "Images differ for type "+biType + " col="+c + + " at x=" + x + " y="+ y + " " + + Integer.toHexString(rgb0) + " vs " + + Integer.toHexString(rgb1)); + } + } + } + + } + public static void main(String[] args) { + new AlphaSurfaceText(BufferedImage.TYPE_INT_ARGB, Color.white); + new AlphaSurfaceText(BufferedImage.TYPE_INT_ARGB, Color.red); + new AlphaSurfaceText(BufferedImage.TYPE_INT_ARGB, Color.blue); + new AlphaSurfaceText(BufferedImage.TYPE_INT_ARGB_PRE, Color.white); + new AlphaSurfaceText(BufferedImage.TYPE_INT_ARGB_PRE, Color.red); + new AlphaSurfaceText(BufferedImage.TYPE_INT_ARGB_PRE, Color.blue); + new AlphaSurfaceText(BufferedImage.TYPE_4BYTE_ABGR, Color.white); + new AlphaSurfaceText(BufferedImage.TYPE_4BYTE_ABGR, Color.red); + new AlphaSurfaceText(BufferedImage.TYPE_4BYTE_ABGR, Color.blue); + new AlphaSurfaceText(BufferedImage.TYPE_4BYTE_ABGR_PRE, Color.white); + new AlphaSurfaceText(BufferedImage.TYPE_4BYTE_ABGR_PRE, Color.red); + new AlphaSurfaceText(BufferedImage.TYPE_4BYTE_ABGR_PRE, Color.blue); + } +} From faadc399660be817fc2bb11219baf64462f9325b Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 30 Apr 2008 13:10:39 -0700 Subject: [PATCH 14/40] 6656651: Windows Look and Feel LCD glyph images have some differences from native applications Reviewed-by: igor, tdv --- jdk/make/sun/font/FILES_c.gmk | 4 +- jdk/make/sun/font/Makefile | 1 + .../classes/sun/font/FileFontStrike.java | 139 ++++- .../share/classes/sun/font/FontManager.java | 6 +- .../share/classes/sun/font/TrueTypeFont.java | 25 + .../sun/awt/Win32GraphicsEnvironment.java | 5 +- jdk/src/windows/native/sun/font/lcdglyph.c | 481 ++++++++++++++++++ .../DrawString/ScaledLCDTextMetrics.java | 82 +++ 8 files changed, 718 insertions(+), 25 deletions(-) create mode 100644 jdk/src/windows/native/sun/font/lcdglyph.c create mode 100644 jdk/test/java/awt/Graphics2D/DrawString/ScaledLCDTextMetrics.java diff --git a/jdk/make/sun/font/FILES_c.gmk b/jdk/make/sun/font/FILES_c.gmk index 6f4c4393e2a..37e00af67fc 100644 --- a/jdk/make/sun/font/FILES_c.gmk +++ b/jdk/make/sun/font/FILES_c.gmk @@ -113,7 +113,9 @@ FILES_cpp_shared = \ ifeq ($(PLATFORM),windows) -FILES_c_platform = fontpath.c +FILES_c_platform = fontpath.c \ + lcdglyph.c + FILES_cpp_platform = D3DTextRenderer.cpp else FILES_c_platform = X11FontScaler.c \ diff --git a/jdk/make/sun/font/Makefile b/jdk/make/sun/font/Makefile index e526efa4dbe..e1d89a7633f 100644 --- a/jdk/make/sun/font/Makefile +++ b/jdk/make/sun/font/Makefile @@ -63,6 +63,7 @@ FILES_export = \ java/awt/Font.java \ java/text/Bidi.java \ sun/font/FileFont.java \ + sun/font/FileFontStrike.java \ sun/font/FontManager.java \ sun/font/GlyphList.java \ sun/font/NativeFont.java \ diff --git a/jdk/src/share/classes/sun/font/FileFontStrike.java b/jdk/src/share/classes/sun/font/FileFontStrike.java index 79890d400ec..aba8861058c 100644 --- a/jdk/src/share/classes/sun/font/FileFontStrike.java +++ b/jdk/src/share/classes/sun/font/FileFontStrike.java @@ -27,6 +27,7 @@ package sun.font; import java.lang.ref.SoftReference; import java.awt.Font; +import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; @@ -105,6 +106,19 @@ public class FileFontStrike extends PhysicalStrike { boolean useNatives; NativeStrike[] nativeStrikes; + /* Used only for communication to native layer */ + private int intPtSize; + + /* Perform global initialisation needed for Windows native rasterizer */ + private static native boolean initNative(); + private static boolean isXPorLater = false; + static { + if (FontManager.isWindows && !FontManager.useT2K && + !GraphicsEnvironment.isHeadless()) { + isXPorLater = initNative(); + } + } + FileFontStrike(FileFont fileFont, FontStrikeDesc desc) { super(fileFont, desc); this.fileFont = fileFont; @@ -165,7 +179,7 @@ public class FileFontStrike extends PhysicalStrike { * should not segment unless there's another reason to do so. */ float ptSize = (float)matrix[3]; // interpreted only when meaningful. - int iSize = (int)ptSize; + int iSize = intPtSize = (int)ptSize; boolean isSimpleTx = (at.getType() & complexTX) == 0; segmentedCache = (numGlyphs > SEGSIZE << 3) || @@ -189,8 +203,26 @@ public class FileFontStrike extends PhysicalStrike { FontManager.deRegisterBadFont(fileFont); return; } - - if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) { + /* First, see if native code should be used to create the glyph. + * GDI will return the integer metrics, not fractional metrics, which + * may be requested for this strike, so we would require here that : + * desc.fmHint != INTVAL_FRACTIONALMETRICS_ON + * except that the advance returned by GDI is always overwritten by + * the JDK rasteriser supplied one (see getGlyphImageFromWindows()). + */ + if (FontManager.isWindows && isXPorLater && + !FontManager.useT2K && + !GraphicsEnvironment.isHeadless() && + !fileFont.useJavaRasterizer && + (desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB || + desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR) && + (matrix[1] == 0.0 && matrix[2] == 0.0 && + matrix[0] == matrix[3] && + matrix[0] >= 3.0 && matrix[0] <= 100.0) && + !((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize)) { + useNatives = true; + } + else if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) { /* Check its a simple scale of a pt size in the range * where native bitmaps typically exist (6-36 pts) */ if (matrix[1] == 0.0 && matrix[2] == 0.0 && @@ -208,7 +240,16 @@ public class FileFontStrike extends PhysicalStrike { } } } - + if (FontManager.logging && FontManager.isWindows) { + FontManager.logger.info + ("Strike for " + fileFont + " at size = " + intPtSize + + " use natives = " + useNatives + + " useJavaRasteriser = " + fileFont.useJavaRasterizer + + " AAHint = " + desc.aaHint + + " Has Embedded bitmaps = " + + ((TrueTypeFont)fileFont). + useEmbeddedBitmapsForSize(intPtSize)); + } this.disposer = new FontStrikeDisposer(fileFont, desc, pScalerContext); /* Always get the image and the advance together for smaller sizes @@ -255,8 +296,50 @@ public class FileFontStrike extends PhysicalStrike { return fileFont.getNumGlyphs(); } - /* Try the native strikes first, then try the fileFont strike */ long getGlyphImageFromNative(int glyphCode) { + if (FontManager.isWindows) { + return getGlyphImageFromWindows(glyphCode); + } else { + return getGlyphImageFromX11(glyphCode); + } + } + + /* There's no global state conflicts, so this method is not + * presently synchronized. + */ + private native long _getGlyphImageFromWindows(String family, + int style, + int size, + int glyphCode, + boolean fracMetrics); + + long getGlyphImageFromWindows(int glyphCode) { + String family = fileFont.getFamilyName(null); + int style = desc.style & Font.BOLD | desc.style & Font.ITALIC + | fileFont.getStyle(); + int size = intPtSize; + long ptr = _getGlyphImageFromWindows + (family, style, size, glyphCode, + desc.fmHint == INTVAL_FRACTIONALMETRICS_ON); + if (ptr != 0) { + /* Get the advance from the JDK rasterizer. This is mostly + * necessary for the fractional metrics case, but there are + * also some very small number (<0.25%) of marginal cases where + * there is some rounding difference between windows and JDK. + * After these are resolved, we can restrict this extra + * work to the FM case. + */ + float advance = getGlyphAdvance(glyphCode, false); + StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset, + advance); + return ptr; + } else { + return fileFont.getGlyphImage(pScalerContext, glyphCode); + } + } + + /* Try the native strikes first, then try the fileFont strike */ + long getGlyphImageFromX11(int glyphCode) { long glyphPtr; char charCode = fileFont.glyphToCharMap[glyphCode]; for (int i=0;i= INVISIBLE_GLYPHS) { return StrikeCache.invisibleGlyphPtr; } - long glyphPtr; + long glyphPtr = 0L; if ((glyphPtr = getCachedGlyphPtr(glyphCode)) != 0L) { return glyphPtr; } else { if (useNatives) { glyphPtr = getGlyphImageFromNative(glyphCode); - } else { + if (glyphPtr == 0L && FontManager.logging) { + FontManager.logger.info + ("Strike for " + fileFont + + " at size = " + intPtSize + + " couldn't get native glyph for code = " + glyphCode); + } + } if (glyphPtr == 0L) { glyphPtr = fileFont.getGlyphImage(pScalerContext, glyphCode); } @@ -300,10 +389,10 @@ public class FileFontStrike extends PhysicalStrike { } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) { continue; } else { - long glyphPtr; + long glyphPtr = 0L; if (useNatives) { glyphPtr = getGlyphImageFromNative(glyphCode); - } else { + } if (glyphPtr == 0L) { glyphPtr = fileFont.getGlyphImage(pScalerContext, glyphCode); } @@ -332,10 +421,11 @@ public class FileFontStrike extends PhysicalStrike { } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) { continue; } else { - long glyphPtr; + long glyphPtr = 0L; if (useNatives) { glyphPtr = getGlyphImageFromNative(glyphCode); - } else { + } + if (glyphPtr == 0L) { glyphPtr = fileFont.getGlyphImage(pScalerContext, glyphCode); } @@ -459,11 +549,16 @@ public class FileFontStrike extends PhysicalStrike { } } + float getGlyphAdvance(int glyphCode) { + return getGlyphAdvance(glyphCode, true); + } + /* Metrics info is always retrieved. If the GlyphInfo address is non-zero * then metrics info there is valid and can just be copied. - * This is in user space coordinates. + * This is in user space coordinates unless getUserAdv == false. + * Device space advance should not be propagated out of this class. */ - float getGlyphAdvance(int glyphCode) { + private float getGlyphAdvance(int glyphCode, boolean getUserAdv) { float advance; if (glyphCode >= INVISIBLE_GLYPHS) { @@ -485,11 +580,11 @@ public class FileFontStrike extends PhysicalStrike { } } - if (invertDevTx != null) { + if (invertDevTx != null || !getUserAdv) { /* If there is a device transform need x & y advance to * transform back into user space. */ - advance = getGlyphMetrics(glyphCode).x; + advance = getGlyphMetrics(glyphCode, getUserAdv).x; } else { long glyphPtr; if (getImageWithAdvance) { @@ -625,6 +720,10 @@ public class FileFontStrike extends PhysicalStrike { } Point2D.Float getGlyphMetrics(int glyphCode) { + return getGlyphMetrics(glyphCode, true); + } + + private Point2D.Float getGlyphMetrics(int glyphCode, boolean getUserAdv) { Point2D.Float metrics = new Point2D.Float(); // !!! or do we force sgv user glyphs? @@ -632,7 +731,7 @@ public class FileFontStrike extends PhysicalStrike { return metrics; } long glyphPtr; - if (getImageWithAdvance) { + if (getImageWithAdvance && getUserAdv) { /* A heuristic optimisation says that for most cases its * worthwhile retrieving the image at the same time as the * metrics. So here we get the image data even if its not @@ -649,9 +748,9 @@ public class FileFontStrike extends PhysicalStrike { metrics.y = StrikeCache.unsafe.getFloat (glyphPtr + StrikeCache.yAdvanceOffset); /* advance is currently in device space, need to convert back - * into user space. + * into user space, unless getUserAdv == false. * This must not include the translation component. */ - if (invertDevTx != null) { + if (invertDevTx != null && getUserAdv) { invertDevTx.deltaTransform(metrics, metrics); } } else { @@ -680,9 +779,9 @@ public class FileFontStrike extends PhysicalStrike { if (value == null) { fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics); /* advance is currently in device space, need to convert back - * into user space. + * into user space, unless getUserAdv == false. */ - if (invertDevTx != null) { + if (invertDevTx != null && getUserAdv) { invertDevTx.deltaTransform(metrics, metrics); } value = new Point2D.Float(metrics.x, metrics.y); diff --git a/jdk/src/share/classes/sun/font/FontManager.java b/jdk/src/share/classes/sun/font/FontManager.java index be059a725d9..32d5da9c24b 100644 --- a/jdk/src/share/classes/sun/font/FontManager.java +++ b/jdk/src/share/classes/sun/font/FontManager.java @@ -244,9 +244,11 @@ public final class FontManager { osName = System.getProperty("os.name", "unknownOS"); isSolaris = osName.startsWith("SunOS"); - if (isSolaris) { - String t2kStr= System.getProperty("sun.java2d.font.scaler"); + String t2kStr = System.getProperty("sun.java2d.font.scaler"); + if (t2kStr != null) { useT2K = "t2k".equals(t2kStr); + } + if (isSolaris) { String version = System.getProperty("os.version", "unk"); isSolaris8 = version.equals("5.8"); isSolaris9 = version.equals("5.9"); diff --git a/jdk/src/share/classes/sun/font/TrueTypeFont.java b/jdk/src/share/classes/sun/font/TrueTypeFont.java index 9e6c46165fe..8a8309efab4 100644 --- a/jdk/src/share/classes/sun/font/TrueTypeFont.java +++ b/jdk/src/share/classes/sun/font/TrueTypeFont.java @@ -893,6 +893,31 @@ public class TrueTypeFont extends FileFont { return null; } + /* Used to determine if this size has embedded bitmaps, which + * for CJK fonts should be used in preference to LCD glyphs. + */ + boolean useEmbeddedBitmapsForSize(int ptSize) { + if (!supportsCJK) { + return false; + } + if (getDirectoryEntry(EBLCTag) == null) { + return false; + } + ByteBuffer eblcTable = getTableBuffer(EBLCTag); + int numSizes = eblcTable.getInt(4); + /* The bitmapSizeTable's start at offset of 8. + * Each bitmapSizeTable entry is 48 bytes. + * The offset of ppemY in the entry is 45. + */ + for (int i=0;i +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "fontscalerdefs.h" + +/* Some of these are also defined in awtmsg.h but I don't want a dependency + * on that here. They are needed here - and in awtmsg.h - until we + * move up our build to define WIN32_WINNT >= 0x501 (ie XP), since MS + * headers will not define them otherwise. + */ +#ifndef SPI_GETFONTSMOOTHINGTYPE +#define SPI_GETFONTSMOOTHINGTYPE 0x200A +#endif //SPI_GETFONTSMOOTHINGTYPE + +#ifndef SPI_GETFONTSMOOTHINGCONTRAST +#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C +#endif //SPI_GETFONTSMOOTHINGCONTRAST + +#ifndef SPI_GETFONTSMOOTHINGORIENTATION +#define SPI_GETFONTSMOOTHINGORIENTATION 0x2012 +#endif //SPI_GETFONTSMOOTHINGORIENTATION + +#ifndef FE_FONTSMOOTHINGORIENTATIONBGR +#define FE_FONTSMOOTHINGORIENTATIONBGR 0x0000 +#endif //FE_FONTSMOOTHINGORIENTATIONBGR + +#ifndef FE_FONTSMOOTHINGORIENTATIONRGB +#define FE_FONTSMOOTHINGORIENTATIONRGB 0x0001 +#endif //FE_FONTSMOOTHINGORIENTATIONRGB + +#define MIN_GAMMA 100 +#define MAX_GAMMA 220 +#define LCDLUTCOUNT (MAX_GAMMA-MIN_GAMMA+1) + +static unsigned char* igLUTable[LCDLUTCOUNT]; + +static unsigned char* getIGTable(int gamma) { + int i, index; + double ig; + char *igTable; + + if (gamma < MIN_GAMMA) { + gamma = MIN_GAMMA; + } else if (gamma > MAX_GAMMA) { + gamma = MAX_GAMMA; + } + + index = gamma - MIN_GAMMA; + + if (igLUTable[index] != NULL) { + return igLUTable[index]; + } + igTable = (unsigned char*)malloc(256); + if (igTable == NULL) { + return NULL; + } + igTable[0] = 0; + igTable[255] = 255; + ig = ((double)gamma)/100.0; + + for (i=1;i<255;i++) { + igTable[i] = (unsigned char)(pow(((double)i)/255.0, ig)*255); + } + igLUTable[index] = igTable; + return igTable; +} + + +JNIEXPORT jboolean JNICALL + Java_sun_font_FileFontStrike_initNative(JNIEnv *env, jclass unused) { + + DWORD osVersion = GetVersion(); + DWORD majorVersion = (DWORD)(LOBYTE(LOWORD(osVersion))); + DWORD minorVersion = (DWORD)(HIBYTE(LOWORD(osVersion))); + + /* Need at least XP which is 5.1 */ + if (majorVersion < 5 || (majorVersion == 5 && minorVersion < 1)) { + return JNI_FALSE; + } + + memset(igLUTable, 0, LCDLUTCOUNT); + + return JNI_TRUE; +} + +#ifndef CLEARTYPE_QUALITY +#define CLEARTYPE_QUALITY 5 +#endif + +#ifndef CLEARTYPE_NATURAL_QUALITY +#define CLEARTYPE_NATURAL_QUALITY 6 +#endif + +#define FREE_AND_RETURN \ + if (hDesktopDC != 0 && hWnd != 0) { \ + ReleaseDC(hWnd, hDesktopDC); \ + }\ + if (hMemoryDC != 0) { \ + DeleteObject(hMemoryDC); \ + } \ + if (hBitmap != 0) { \ + DeleteObject(hBitmap); \ + } \ + if (dibImage != NULL) { \ + free(dibImage); \ + } \ + if (glyphInfo != NULL) { \ + free(glyphInfo); \ + } \ + return (jlong)0; +/* end define */ + +JNIEXPORT jlong JNICALL +Java_sun_font_FileFontStrike__1getGlyphImageFromWindows +(JNIEnv *env, jobject unused, + jstring fontFamily, jint style, jint size, jint glyphCode, jboolean fm) { + + GLYPHMETRICS glyphMetrics; + LOGFONTW lf; + BITMAPINFO bmi; + TEXTMETRIC textMetric; + RECT rect; + int bytesWidth, dibBytesWidth, extra, imageSize, dibImageSize; + unsigned char* dibImage = NULL, *rowPtr, *pixelPtr, *dibPixPtr, *dibRowPtr; + unsigned char r,g,b; + unsigned char* igTable; + GlyphInfo* glyphInfo = NULL; + int nameLen; + LPWSTR name; + HFONT oldFont, hFont; + MAT2 mat2; + + unsigned short width; + unsigned short height; + short advanceX; + short advanceY; + int topLeftX; + int topLeftY; + int err; + int bmWidth, bmHeight; + int x, y; + HBITMAP hBitmap = NULL, hOrigBM; + int gamma, orient; + + HWND hWnd = NULL; + HDC hDesktopDC = NULL; + HDC hMemoryDC = NULL; + + hWnd = GetDesktopWindow(); + hDesktopDC = GetWindowDC(hWnd); + if (hDesktopDC == NULL) { + return (jlong)0; + } + if (GetDeviceCaps(hDesktopDC, BITSPIXEL) < 15) { + FREE_AND_RETURN; + } + + hMemoryDC = CreateCompatibleDC(hDesktopDC); + if (hMemoryDC == NULL || fontFamily == NULL) { + FREE_AND_RETURN; + } + err = SetMapMode(hMemoryDC, MM_TEXT); + if (err == 0) { + FREE_AND_RETURN; + } + + memset(&lf, 0, sizeof(LOGFONTW)); + lf.lfHeight = -size; + lf.lfWeight = (style & 1) ? FW_BOLD : FW_NORMAL; + lf.lfItalic = (style & 2) ? 0xff : 0; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfQuality = CLEARTYPE_QUALITY; + lf.lfOutPrecision = OUT_TT_PRECIS; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfPitchAndFamily = DEFAULT_PITCH; + + nameLen = (*env)->GetStringLength(env, fontFamily); + name = (LPWSTR)alloca((nameLen+1)*2); + if (name == NULL) { + FREE_AND_RETURN; + } + (*env)->GetStringRegion(env, fontFamily, 0, nameLen, name); + name[nameLen] = '\0'; + + if (nameLen < (sizeof(lf.lfFaceName) / sizeof(lf.lfFaceName[0]))) { + wcscpy(lf.lfFaceName, name); + } else { + FREE_AND_RETURN; + } + + hFont = CreateFontIndirectW(&lf); + if (hFont == NULL) { + FREE_AND_RETURN; + } + oldFont = SelectObject(hMemoryDC, hFont); + + memset(&textMetric, 0, sizeof(TEXTMETRIC)); + err = GetTextMetrics(hMemoryDC, &textMetric); + if (err == 0) { + FREE_AND_RETURN; + } + memset(&glyphMetrics, 0, sizeof(GLYPHMETRICS)); + memset(&mat2, 0, sizeof(MAT2)); + mat2.eM11.value = 1; mat2.eM22.value = 1; + err = GetGlyphOutline(hMemoryDC, glyphCode, + GGO_METRICS|GGO_GLYPH_INDEX, + &glyphMetrics, + 0, NULL, &mat2); + if (err == GDI_ERROR) { + /* Probably no such glyph - ie the font wasn't the one we expected. */ + FREE_AND_RETURN; + } + + width = (unsigned short)glyphMetrics.gmBlackBoxX; + height = (unsigned short)glyphMetrics.gmBlackBoxY; + + /* Don't handle "invisible" glyphs in this code */ + if (width <= 0 || height == 0) { + FREE_AND_RETURN; + } + + advanceX = glyphMetrics.gmCellIncX; + advanceY = glyphMetrics.gmCellIncY; + topLeftX = glyphMetrics.gmptGlyphOrigin.x; + topLeftY = glyphMetrics.gmptGlyphOrigin.y; + + /* GetGlyphOutline pre-dates cleartype and I'm not sure that it will + * account for all pixels touched by the rendering. Need to widen, + * and also adjust by one the x position at which it is rendered. + * The extra pixels of width are used as follows : + * One extra pixel at the left and the right will be needed to absorb + * the pixels that will be touched by filtering by GDI to compensate + * for colour fringing. + * However there seem to be some cases where GDI renders two extra + * pixels to the right, so we add one additional pixel to the right, + * and in the code that copies this to the image cache we test for + * the (rare) cases when this is touched, and if its not reduce the + * stated image width for the blitting loops. + * For fractional metrics : + * One extra pixel at each end to account for sub-pixel positioning used + * when fractional metrics is on in LCD mode. + * The pixel at the left is needed so the blitting loop can index into + * that a byte at a time to more accurately position the glyph. + * The pixel at the right is needed so that when such indexing happens, + * the blitting still can use the same width. + * Consequently the width that is specified for the glyph is one less + * than that of the actual image. + * Note that in the FM case as a consequence we need to adjust the + * position at which GDI renders, and the declared width of the glyph + * See the if (fm) {} cases in the code. + * For the non-FM case, we not only save 3 bytes per row, but this + * prevents apparent glyph overlapping which affects the rendering + * performance of accelerated pipelines since it adds additional + * read-back requirements. + */ + width+=3; + if (fm) { + width+=1; + } + /* DIB scanline must end on a DWORD boundary. We specify 3 bytes per pixel, + * so must round up as needed to a multiple of 4 bytes. + */ + dibBytesWidth = bytesWidth = width*3; + extra = dibBytesWidth % 4; + if (extra != 0) { + dibBytesWidth += (4-extra); + } + /* The glyph cache image must be a multiple of 3 bytes wide. */ + extra = bytesWidth % 3; + if (extra != 0) { + bytesWidth += (3-extra); + } + bmWidth = width; + bmHeight = height; + + /* Must use desktop DC to create a bitmap of that depth */ + hBitmap = CreateCompatibleBitmap(hDesktopDC, bmWidth, bmHeight); + if (hBitmap == NULL) { + FREE_AND_RETURN; + } + hOrigBM = (HBITMAP)SelectObject(hMemoryDC, hBitmap); + + /* Fill in black */ + rect.left = 0; + rect.top = 0; + rect.right = bmWidth; + rect.bottom = bmHeight; + FillRect(hMemoryDC, (LPRECT)&rect, GetStockObject(BLACK_BRUSH)); + + /* Set text color to white, background to black. */ + SetBkColor(hMemoryDC, RGB(0,0,0)); + SetTextColor(hMemoryDC, RGB(255,255,255)); + + /* adjust rendering position */ + x = -topLeftX+1; + if (fm) { + x += 1; + } + y = topLeftY - textMetric.tmAscent; + err = ExtTextOutW(hMemoryDC, x, y, ETO_GLYPH_INDEX|ETO_OPAQUE, + (LPRECT)&rect, (LPCWSTR)&glyphCode, 1, NULL); + if (err == 0) { + FREE_AND_RETURN; + } + + /* Now get the image into a DIB. + * MS docs for GetDIBits says the compatible bitmap must not be + * selected into a DC, so restore the original first. + */ + SelectObject(hMemoryDC, hOrigBM); + SelectObject(hMemoryDC, oldFont); + DeleteObject(hFont); + + memset(&bmi, 0, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; + bmi.bmiHeader.biCompression = BI_RGB; + + dibImageSize = dibBytesWidth*height; + dibImage = malloc(dibImageSize); + if (dibImage == NULL) { + FREE_AND_RETURN; + } + memset(dibImage, 0, dibImageSize); + + err = GetDIBits(hMemoryDC, hBitmap, 0, height, dibImage, + &bmi, DIB_RGB_COLORS); + + if (err == 0) { /* GetDIBits failed. */ + FREE_AND_RETURN; + } + + err = SystemParametersInfo(SPI_GETFONTSMOOTHINGORIENTATION, 0, &orient, 0); + if (err == 0) { + FREE_AND_RETURN; + } + err = SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &gamma, 0); + if (err == 0) { + FREE_AND_RETURN; + } + igTable = getIGTable(gamma/10); + if (igTable == NULL) { + FREE_AND_RETURN; + } + + /* Now copy glyph image into a GlyphInfo structure and return it. + * NB the xadvance calculated here may be overwritten by the caller. + * 1 is subtracted from the bitmap width to get the glyph width, since + * that extra "1" was added as padding, so the sub-pixel positioning of + * fractional metrics could index into it. + */ + imageSize = bytesWidth*height; + glyphInfo = (GlyphInfo*)malloc(sizeof(GlyphInfo)+imageSize); + if (malloc == NULL) { + FREE_AND_RETURN; + } + glyphInfo->cellInfo = NULL; + glyphInfo->rowBytes = bytesWidth; + glyphInfo->width = width; + if (fm) { + glyphInfo->width -= 1; // must subtract 1 + } + glyphInfo->height = height; + glyphInfo->advanceX = advanceX; + glyphInfo->advanceY = advanceY; + glyphInfo->topLeftX = (float)(topLeftX-1); + if (fm) { + glyphInfo->topLeftX -= 1; + } + glyphInfo->topLeftY = (float)-topLeftY; + glyphInfo->image = (unsigned char*)glyphInfo+sizeof(GlyphInfo); + memset(glyphInfo->image, 0, imageSize); + + /* DIB 24bpp data is always stored in BGR order, but we usually + * need this in RGB, so we can't just memcpy and need to swap B and R. + * Also need to apply inverse gamma adjustment here. + * We re-use the variable "extra" to see if the last pixel is touched + * at all. If its not we can reduce the glyph image width. This comes + * into play in some cases where GDI touches more pixels than accounted + * for by increasing width by two pixels over the B&W image. Whilst + * the bytes are in the cache, it doesn't affect rendering performance + * of the hardware pipelines. + */ + extra = 0; + if (fm) { + extra = 1; // always need it. + } + dibRowPtr = dibImage; + rowPtr = glyphInfo->image; + for (y=0;ywidth -= 1; + } + + free(dibImage); + ReleaseDC(hWnd, hDesktopDC); + DeleteObject(hMemoryDC); + DeleteObject(hBitmap); + + return ptr_to_jlong(glyphInfo); +} diff --git a/jdk/test/java/awt/Graphics2D/DrawString/ScaledLCDTextMetrics.java b/jdk/test/java/awt/Graphics2D/DrawString/ScaledLCDTextMetrics.java new file mode 100644 index 00000000000..7aa6694758b --- /dev/null +++ b/jdk/test/java/awt/Graphics2D/DrawString/ScaledLCDTextMetrics.java @@ -0,0 +1,82 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 6685312 + * @summary Check advance of LCD text on a scaled graphics. + */ + +import javax.swing.*; +import java.awt.*; +import static java.awt.RenderingHints.*; + +public class ScaledLCDTextMetrics extends Component { + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.add("Center", new ScaledLCDTextMetrics()); + f.pack(); + f.setVisible(true); + } + + public Dimension getPreferredSize() { + return new Dimension(200,100); + } + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D)g; + + Font f = new Font("Tahoma", Font.PLAIN, 11); + g.setFont(f); + g.setColor(Color.white); + g.fillRect(0,0,400,300); + g.setColor(Color.black); + g2.setRenderingHint(KEY_TEXT_ANTIALIASING,VALUE_TEXT_ANTIALIAS_LCD_HRGB); + String text = "ABCDEFGHIJKLI"; + + FontMetrics fm1 = g2.getFontMetrics(); + int adv1 = fm1.stringWidth(text); + g.drawString(text, 5, 20); + + g2.scale(2,2); + + FontMetrics fm2 = g2.getFontMetrics(); + int adv2 = fm2.stringWidth(text); + g.drawString(text, 5, 40); + + double frac = Math.abs(adv1/(double)adv2); + + System.out.println("scalex1: " + adv1); + System.out.println("scalex2: " + adv2); + System.out.println("Fraction : "+ frac); + + // adv1 will not be exactly the same as adv2, but should differ + // only by a fraction. + + if (frac < 0.8 || frac > 1.2) { + throw new RuntimeException("Metrics differ " + + "Adv1="+adv1+" Adv2="+adv2+" Fraction="+frac); + } + } +} From 6014ce6668d0a4b9366507c40bdfbcdb3480c6e7 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Tue, 13 May 2008 16:18:30 -0700 Subject: [PATCH 15/40] 6699843: IllegalArgumentException when using Graphics.drawString( "", 0, 0 ) Reviewed-by: igor, tdv --- .../classes/sun/java2d/SunGraphics2D.java | 18 ++++++ .../DrawString/EmptyAttrString.java | 58 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 jdk/test/java/awt/Graphics2D/DrawString/EmptyAttrString.java diff --git a/jdk/src/share/classes/sun/java2d/SunGraphics2D.java b/jdk/src/share/classes/sun/java2d/SunGraphics2D.java index 887f4c91e97..921ece4150e 100644 --- a/jdk/src/share/classes/sun/java2d/SunGraphics2D.java +++ b/jdk/src/share/classes/sun/java2d/SunGraphics2D.java @@ -2805,6 +2805,9 @@ public final class SunGraphics2D } if (font.hasLayoutAttributes()) { + if (str.length() == 0) { + return; + } new TextLayout(str, font, getFontRenderContext()).draw(this, x, y); return; } @@ -2831,6 +2834,9 @@ public final class SunGraphics2D } if (font.hasLayoutAttributes()) { + if (str.length() == 0) { + return; + } new TextLayout(str, font, getFontRenderContext()).draw(this, x, y); return; } @@ -2856,6 +2862,9 @@ public final class SunGraphics2D if (iterator == null) { throw new NullPointerException("AttributedCharacterIterator is null"); } + if (iterator.getBeginIndex() == iterator.getEndIndex()) { + return; /* nothing to draw */ + } TextLayout tl = new TextLayout(iterator, getFontRenderContext()); tl.draw(this, (float) x, (float) y); } @@ -2865,6 +2874,9 @@ public final class SunGraphics2D if (iterator == null) { throw new NullPointerException("AttributedCharacterIterator is null"); } + if (iterator.getBeginIndex() == iterator.getEndIndex()) { + return; /* nothing to draw */ + } TextLayout tl = new TextLayout(iterator, getFontRenderContext()); tl.draw(this, x, y); } @@ -2900,6 +2912,9 @@ public final class SunGraphics2D throw new ArrayIndexOutOfBoundsException("bad offset/length"); } if (font.hasLayoutAttributes()) { + if (data.length == 0) { + return; + } new TextLayout(new String(data, offset, length), font, getFontRenderContext()).draw(this, x, y); return; @@ -2934,6 +2949,9 @@ public final class SunGraphics2D chData[i] = (char)(data[i+offset] & 0xff); } if (font.hasLayoutAttributes()) { + if (data.length == 0) { + return; + } new TextLayout(new String(chData), font, getFontRenderContext()).draw(this, x, y); return; diff --git a/jdk/test/java/awt/Graphics2D/DrawString/EmptyAttrString.java b/jdk/test/java/awt/Graphics2D/DrawString/EmptyAttrString.java new file mode 100644 index 00000000000..c3c75c79537 --- /dev/null +++ b/jdk/test/java/awt/Graphics2D/DrawString/EmptyAttrString.java @@ -0,0 +1,58 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 6699843 + * @summary IllegalArgumentException when using Graphics.drawString( "", 0, 0 ) + */ + +import java.awt.*; +import java.awt.font.*; +import java.awt.image.*; +import java.text.*; +import java.util.*; + +public class EmptyAttrString { + + public static void main(String[] args) { + BufferedImage bi = + new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + Graphics2D g = bi.createGraphics(); + Font f = new Font( "Dialog", Font.PLAIN, 12 ); + Map map = new HashMap(); + map.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); + f = f.deriveFont(map); + g.setFont(f); + g.drawString("", 50, 50); + g.drawString("", 50f, 50f); + char[] chs = { } ; + g.drawChars(chs, 0, 0, 50, 50); + byte[] bytes = { } ; + g.drawBytes(bytes, 0, 0, 50, 50); + AttributedString astr = new AttributedString(""); + g.drawString(astr.getIterator(), 50, 50); + g.drawString(astr.getIterator(), 50f, 50f); + return; + } +} From 95f191ea9571fcb3b7c927dc316ae99609838eff Mon Sep 17 00:00:00 2001 From: Dan Munckton Date: Tue, 13 May 2008 16:46:06 -0700 Subject: [PATCH 16/40] 6636469: Java Fullscreen Exclusive Mode not working with Xorg server 1.3.0 and above Improve the check for full exclusive screen support by analyzing RANDR extension version Reviewed-by: tdv, prr --- .../solaris/native/sun/awt/awt_GraphicsEnv.c | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.c b/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.c index b3e52ee7790..68299f79602 100644 --- a/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.c +++ b/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.c @@ -1626,6 +1626,8 @@ Java_sun_awt_X11GraphicsEnvironment_getXineramaCenterPoint(JNIEnv *env, #define BIT_DEPTH_MULTI java_awt_DisplayMode_BIT_DEPTH_MULTI +typedef Status + (*XRRQueryVersionType) (Display *dpy, int *major_versionp, int *minor_versionp); typedef XRRScreenConfiguration* (*XRRGetScreenInfoType)(Display *dpy, Drawable root); typedef void @@ -1650,6 +1652,7 @@ typedef Status short rate, Time timestamp); +static XRRQueryVersionType awt_XRRQueryVersion; static XRRGetScreenInfoType awt_XRRGetScreenInfo; static XRRFreeScreenConfigInfoType awt_XRRFreeScreenConfigInfo; static XRRConfigRatesType awt_XRRConfigRates; @@ -1672,6 +1675,8 @@ static XRRSetScreenConfigAndRateType awt_XRRSetScreenConfigAndRate; static jboolean X11GD_InitXrandrFuncs(JNIEnv *env) { + int rr_maj_ver = 0, rr_min_ver = 0; + void *pLibRandR = dlopen("libXrandr.so.2", RTLD_LAZY | RTLD_LOCAL); if (pLibRandR == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, @@ -1679,6 +1684,41 @@ X11GD_InitXrandrFuncs(JNIEnv *env) return JNI_FALSE; } + LOAD_XRANDR_FUNC(XRRQueryVersion); + + if (!(*awt_XRRQueryVersion)(awt_display, &rr_maj_ver, &rr_min_ver)) { + J2dRlsTraceLn(J2D_TRACE_ERROR, + "X11GD_InitXrandrFuncs: XRRQueryVersion returned an error status"); + dlclose(pLibRandR); + return JNI_FALSE; + } + + if (usingXinerama) { + /* + * We can proceed as long as this is RANDR 1.2 or above. + * As of Xorg server 1.3 onwards the Xinerama backend may actually be + * a fake one provided by RANDR itself. See Java bug 6636469 for info. + */ + if (!(rr_maj_ver > 1 || (rr_maj_ver == 1 && rr_min_ver >= 2))) { + J2dRlsTraceLn2(J2D_TRACE_INFO, "X11GD_InitXrandrFuncs: Can't use Xrandr. " + "Xinerama is active and Xrandr version is %d.%d", + rr_maj_ver, rr_min_ver); + dlclose(pLibRandR); + return JNI_FALSE; + } + + /* + * REMIND: Fullscreen mode doesn't work quite right with multi-monitor + * setups and RANDR 1.2. So for now we also require a single screen. + */ + if (awt_numScreens > 1 ) { + J2dRlsTraceLn(J2D_TRACE_INFO, "X11GD_InitXrandrFuncs: Can't use Xrandr. " + "Multiple screens in use"); + dlclose(pLibRandR); + return JNI_FALSE; + } + } + LOAD_XRANDR_FUNC(XRRGetScreenInfo); LOAD_XRANDR_FUNC(XRRFreeScreenConfigInfo); LOAD_XRANDR_FUNC(XRRConfigRates); @@ -1814,15 +1854,6 @@ Java_sun_awt_X11GraphicsDevice_initXrandrExtension int opcode = 0, firstEvent = 0, firstError = 0; jboolean ret; - if (usingXinerama) { - /* - * REMIND: we'll just punt if Xinerama is enabled; we can remove this - * restriction in the future if we find Xinerama and XRandR playing - * well together... - */ - return JNI_FALSE; - } - AWT_LOCK(); ret = (jboolean)XQueryExtension(awt_display, "RANDR", &opcode, &firstEvent, &firstError); From 8011082abfbc99e458441c2c2047d43e1a1fec48 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Tue, 13 May 2008 16:49:21 -0700 Subject: [PATCH 17/40] 6696292: Printing transformed images accuracy problems Reviewed-by: jgodinez, igor --- jdk/src/share/classes/sun/print/PSPathGraphics.java | 11 +++++++++-- .../classes/sun/awt/windows/WPathGraphics.java | 12 ++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/jdk/src/share/classes/sun/print/PSPathGraphics.java b/jdk/src/share/classes/sun/print/PSPathGraphics.java index d4518eea8a6..daf3bb7e217 100644 --- a/jdk/src/share/classes/sun/print/PSPathGraphics.java +++ b/jdk/src/share/classes/sun/print/PSPathGraphics.java @@ -344,8 +344,15 @@ class PSPathGraphics extends PathGraphics { double devScaleX = devResX / DEFAULT_USER_RES; double devScaleY = devResY / DEFAULT_USER_RES; - if (scaleX > devScaleX) scaleX = devScaleX; - if (scaleY > devScaleY) scaleY = devScaleY; + /* check if rotated or sheared */ + int transformType = fullTransform.getType(); + boolean clampScale = ((transformType & + (AffineTransform.TYPE_GENERAL_ROTATION | + AffineTransform.TYPE_GENERAL_TRANSFORM)) != 0); + if (clampScale) { + if (scaleX > devScaleX) scaleX = devScaleX; + if (scaleY > devScaleY) scaleY = devScaleY; + } /* We do not need to draw anything if either scaling * factor is zero. diff --git a/jdk/src/windows/classes/sun/awt/windows/WPathGraphics.java b/jdk/src/windows/classes/sun/awt/windows/WPathGraphics.java index 9674572cc9d..e94bd616b98 100644 --- a/jdk/src/windows/classes/sun/awt/windows/WPathGraphics.java +++ b/jdk/src/windows/classes/sun/awt/windows/WPathGraphics.java @@ -943,8 +943,16 @@ class WPathGraphics extends PathGraphics { double devResY = wPrinterJob.getYRes(); double devScaleX = devResX / DEFAULT_USER_RES; double devScaleY = devResY / DEFAULT_USER_RES; - if (scaleX > devScaleX) scaleX = devScaleX; - if (scaleY > devScaleY) scaleY = devScaleY; + + /* check if rotated or sheared */ + int transformType = fullTransform.getType(); + boolean clampScale = ((transformType & + (AffineTransform.TYPE_GENERAL_ROTATION | + AffineTransform.TYPE_GENERAL_TRANSFORM)) != 0); + if (clampScale) { + if (scaleX > devScaleX) scaleX = devScaleX; + if (scaleY > devScaleY) scaleY = devScaleY; + } /* We do not need to draw anything if either scaling * factor is zero. From 33dda32d9ca2287a4301ab53c3e8bcc78acbf305 Mon Sep 17 00:00:00 2001 From: Douglas Felt Date: Tue, 13 May 2008 16:56:22 -0700 Subject: [PATCH 18/40] 6697721: OpenJDK: rotated text baseline different between TextLayout and drawString Reviewed-by: prr, igor --- .../share/native/sun/font/freetypeScaler.c | 23 ++++++++++++++----- .../Graphics2D/DrawString/RotTransText.java | 12 ++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/jdk/src/share/native/sun/font/freetypeScaler.c b/jdk/src/share/native/sun/font/freetypeScaler.c index 1ce56123dfa..051e7c4c4a0 100644 --- a/jdk/src/share/native/sun/font/freetypeScaler.c +++ b/jdk/src/share/native/sun/font/freetypeScaler.c @@ -368,7 +368,7 @@ Java_sun_font_FreetypeFontScaler_createScalerContextNative( //text can not be smaller than 1 point ptsz = 1.0; } - context->ptsz = (((int) ptsz) << 6); + context->ptsz = (int)(ptsz * 64); context->transform.xx = FloatToFTFixed((float)dmat[0]/ptsz); context->transform.yx = -FloatToFTFixed((float)dmat[1]/ptsz); context->transform.xy = -FloatToFTFixed((float)dmat[2]/ptsz); @@ -779,13 +779,24 @@ Java_sun_font_FreetypeFontScaler_getGlyphImageNative( } if (context->fmType == TEXT_FM_ON) { - glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x); - glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y); - } else { + double advh = FTFixedToFloat(ftglyph->linearHoriAdvance); glyphInfo->advanceX = - (float) ROUND(FT26Dot6ToFloat(ftglyph->advance.x)); + (float) (advh * FTFixedToFloat(context->transform.xx)); glyphInfo->advanceY = - (float) ROUND(FT26Dot6ToFloat(-ftglyph->advance.y)); + (float) (advh * FTFixedToFloat(context->transform.xy)); + } else { + if (!ftglyph->advance.y) { + glyphInfo->advanceX = + (float) ROUND(FT26Dot6ToFloat(ftglyph->advance.x)); + glyphInfo->advanceY = 0; + } else if (!ftglyph->advance.x) { + glyphInfo->advanceX = 0; + glyphInfo->advanceY = + (float) ROUND(FT26Dot6ToFloat(-ftglyph->advance.y)); + } else { + glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x); + glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y); + } } if (imageSize == 0) { diff --git a/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java b/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java index 9f38fa113f2..87b82290705 100644 --- a/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java +++ b/jdk/test/java/awt/Graphics2D/DrawString/RotTransText.java @@ -38,12 +38,24 @@ public class RotTransText { public static void main(String[] args) { + testIt(false); + testIt(true); + + } + + public static void testIt(boolean fmOn) { + int wid=400, hgt=400; BufferedImage bi = new BufferedImage(wid, hgt, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = bi.createGraphics(); + if (fmOn) { + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } + int x=130, y=130; String s = "Text"; From 3ea05e6a1a6d04225ee2f5c3d0bdb65bb5bc5cd8 Mon Sep 17 00:00:00 2001 From: Dmitri Trembovetski Date: Wed, 14 May 2008 09:16:18 -0700 Subject: [PATCH 19/40] 6604044: java crashes talking to second X screen Reviewed-by: prr --- jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.c b/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.c index 68299f79602..7f64a882fb4 100644 --- a/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.c +++ b/jdk/src/solaris/native/sun/awt/awt_GraphicsEnv.c @@ -650,7 +650,7 @@ static void xinerama_init_linux() if (XineramaQueryScreens != NULL) { DTRACE_PRINTLN("calling XineramaQueryScreens func on Linux"); xinInfo = (*XineramaQueryScreens)(awt_display, &locNumScr); - if (xinInfo != NULL) { + if (xinInfo != NULL && locNumScr > XScreenCount(awt_display)) { int32_t idx; DTRACE_PRINTLN("Enabling Xinerama support"); usingXinerama = True; @@ -701,7 +701,8 @@ static void xinerama_init_solaris() if (XineramaSolarisFunc != NULL) { DTRACE_PRINTLN("calling XineramaGetInfo func on Solaris"); if ((*XineramaSolarisFunc)(awt_display, 0, &fbrects[0], - &fbhints[0], &locNumScr) != 0) + &fbhints[0], &locNumScr) != 0 && + locNumScr > XScreenCount(awt_display)) { DTRACE_PRINTLN("Enabling Xinerama support"); usingXinerama = True; From f3ee9c2f0609d2b975f273d695c64e349bed3f15 Mon Sep 17 00:00:00 2001 From: Vinnie Ryan Date: Wed, 14 May 2008 18:59:18 +0100 Subject: [PATCH 20/40] 6383078: OCSP checking does not work on end-entity certificate Reviewed-by: mullan --- .../provider/certpath/OCSPChecker.java | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java b/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java index 05146c8e5d6..7918a641b7e 100644 --- a/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java +++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java @@ -102,7 +102,7 @@ class OCSPChecker extends PKIXCertPathChecker { */ public void init(boolean forward) throws CertPathValidatorException { if (!forward) { - remainingCerts = certs.length; + remainingCerts = certs.length + 1; } else { throw new CertPathValidatorException( "Forward checking not supported"); @@ -131,14 +131,22 @@ class OCSPChecker extends PKIXCertPathChecker { InputStream in = null; OutputStream out = null; + + // Decrement the certificate counter + remainingCerts--; + try { - // Examine OCSP properties X509Certificate responderCert = null; boolean seekResponderCert = false; X500Principal responderSubjectName = null; X500Principal responderIssuerName = null; BigInteger responderSerialNumber = null; + boolean seekIssuerCert = true; + X509CertImpl issuerCertImpl = null; + X509CertImpl currCertImpl = + X509CertImpl.toImpl((X509Certificate)cert); + /* * OCSP security property values, in the following order: * 1. ocsp.responderURL @@ -148,6 +156,9 @@ class OCSPChecker extends PKIXCertPathChecker { */ String[] properties = getOCSPProperties(); + // Check whether OCSP is feasible before seeking cert information + URL url = getOCSPServerURL(currCertImpl, properties); + // When responder's subject name is set then the issuer/serial // properties are ignored if (properties[1] != null) { @@ -172,14 +183,9 @@ class OCSPChecker extends PKIXCertPathChecker { seekResponderCert = true; } - boolean seekIssuerCert = true; - X509CertImpl issuerCertImpl = null; - X509CertImpl currCertImpl = - X509CertImpl.toImpl((X509Certificate)cert); - remainingCerts--; - - // Set the issuer certificate - if (remainingCerts != 0) { + // Set the issuer certificate to the next cert in the chain + // (unless we're processing the final cert). + if (remainingCerts < certs.length) { issuerCertImpl = X509CertImpl.toImpl(certs[remainingCerts]); seekIssuerCert = false; // done @@ -312,7 +318,8 @@ class OCSPChecker extends PKIXCertPathChecker { // Construct an OCSP Request OCSPRequest ocspRequest = new OCSPRequest(currCertImpl, issuerCertImpl); - URL url = getOCSPServerURL(currCertImpl, properties); + + // Use the URL to the OCSP service that was created earlier HttpURLConnection con = (HttpURLConnection)url.openConnection(); if (DEBUG != null) { DEBUG.println("connecting to OCSP service at: " + url); From f333c99e58f20ad9ff1289a55aaeec0f4a20d8c0 Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Wed, 14 May 2008 16:05:07 -0700 Subject: [PATCH 21/40] 6675596: SurfaceManagerFactory should allow plugging in different implementations Reviewed-by: tdv, campbell --- .../sun/awt/image/SunVolatileImage.java | 3 +- .../sun/java2d/SurfaceManagerFactory.java | 91 +++++++++++++++++++ .../sun/awt/X11GraphicsEnvironment.java | 6 ++ ...ry.java => UnixSurfaceManagerFactory.java} | 21 ++--- .../sun/awt/Win32GraphicsEnvironment.java | 5 + ...java => WindowsSurfaceManagerFactory.java} | 16 ++-- 6 files changed, 122 insertions(+), 20 deletions(-) create mode 100644 jdk/src/share/classes/sun/java2d/SurfaceManagerFactory.java rename jdk/src/solaris/classes/sun/java2d/{SurfaceManagerFactory.java => UnixSurfaceManagerFactory.java} (80%) rename jdk/src/windows/classes/sun/java2d/{SurfaceManagerFactory.java => WindowsSurfaceManagerFactory.java} (83%) diff --git a/jdk/src/share/classes/sun/awt/image/SunVolatileImage.java b/jdk/src/share/classes/sun/awt/image/SunVolatileImage.java index 875201d1bec..d0c18f38689 100644 --- a/jdk/src/share/classes/sun/awt/image/SunVolatileImage.java +++ b/jdk/src/share/classes/sun/awt/image/SunVolatileImage.java @@ -165,7 +165,8 @@ public class SunVolatileImage extends VolatileImage { { return new BufImgVolatileSurfaceManager(this, context); } - return SurfaceManagerFactory.createVolatileManager(this, context); + SurfaceManagerFactory smf = SurfaceManagerFactory.getInstance(); + return smf.createVolatileManager(this, context); } private Color getForeground() { diff --git a/jdk/src/share/classes/sun/java2d/SurfaceManagerFactory.java b/jdk/src/share/classes/sun/java2d/SurfaceManagerFactory.java new file mode 100644 index 00000000000..4f3aa15095a --- /dev/null +++ b/jdk/src/share/classes/sun/java2d/SurfaceManagerFactory.java @@ -0,0 +1,91 @@ +/* + * Copyright 2003-2008 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.java2d; + +import sun.awt.image.SunVolatileImage; +import sun.awt.image.VolatileSurfaceManager; + +/** + * This factory creates platform specific VolatileSurfaceManager + * implementations. + * + * There are two platform specific SurfaceManagerFactories in OpenJDK, + * UnixSurfaceManagerFactory and WindowsSurfaceManagerFactory. + * The actually used SurfaceManagerFactory is set by the respective platform + * GraphicsEnvironment implementations in the static initializer. + */ +public abstract class SurfaceManagerFactory { + + /** + * The single shared instance. + */ + private static SurfaceManagerFactory instance; + + /** + * Returns the surface manager factory instance. This returns a factory + * that has been set by {@link #setInstance(SurfaceManagerFactory)}. + * + * @return the surface manager factory + */ + public synchronized static SurfaceManagerFactory getInstance() { + + if (instance == null) { + throw new IllegalStateException("No SurfaceManagerFactory set."); + } + return instance; + } + + /** + * Sets the surface manager factory. This may only be called once, and it + * may not be set back to {@code null} when the factory is already + * instantiated. + * + * @param factory the factory to set + */ + public synchronized static void setInstance(SurfaceManagerFactory factory) { + + if (factory == null) { + // We don't want to allow setting this to null at any time. + throw new IllegalArgumentException("factory must be non-null"); + } + + if (instance != null) { + // We don't want to re-set the instance at any time. + throw new IllegalStateException("The surface manager factory is already initialized"); + } + + instance = factory; + } + + /** + * Creates a new instance of a VolatileSurfaceManager given any + * arbitrary SunVolatileImage. An optional context Object can be supplied + * as a way for the caller to pass pipeline-specific context data to + * the VolatileSurfaceManager (such as a backbuffer handle, for example). + */ + public abstract VolatileSurfaceManager + createVolatileManager(SunVolatileImage image, Object context); +} diff --git a/jdk/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java b/jdk/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java index 27ba044c270..b531fa739b9 100644 --- a/jdk/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java +++ b/jdk/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java @@ -48,6 +48,8 @@ import sun.font.Font2D; import sun.font.FontManager; import sun.font.NativeFont; import sun.java2d.SunGraphicsEnvironment; +import sun.java2d.SurfaceManagerFactory; +import sun.java2d.UnixSurfaceManagerFactory; /** * This is an implementation of a GraphicsEnvironment object for the @@ -177,6 +179,10 @@ public class X11GraphicsEnvironment return null; } }); + + // Install the correct surface manager factory. + SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory()); + } private static boolean glxAvailable; diff --git a/jdk/src/solaris/classes/sun/java2d/SurfaceManagerFactory.java b/jdk/src/solaris/classes/sun/java2d/UnixSurfaceManagerFactory.java similarity index 80% rename from jdk/src/solaris/classes/sun/java2d/SurfaceManagerFactory.java rename to jdk/src/solaris/classes/sun/java2d/UnixSurfaceManagerFactory.java index 525ec325a59..719ae69c15b 100644 --- a/jdk/src/solaris/classes/sun/java2d/SurfaceManagerFactory.java +++ b/jdk/src/solaris/classes/sun/java2d/UnixSurfaceManagerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2008 Sun Microsystems, Inc. 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 @@ -23,24 +23,23 @@ * have any questions. */ + package sun.java2d; import java.awt.GraphicsConfiguration; -import java.awt.image.BufferedImage; -import sun.awt.X11GraphicsConfig; + import sun.awt.image.SunVolatileImage; -import sun.awt.image.SurfaceManager; import sun.awt.image.VolatileSurfaceManager; import sun.java2d.opengl.GLXGraphicsConfig; import sun.java2d.opengl.GLXVolatileSurfaceManager; import sun.java2d.x11.X11VolatileSurfaceManager; /** - * This is a factory class with static methods for creating a - * platform-specific instance of a particular SurfaceManager. Each platform - * (Windows, Unix, etc.) has its own specialized SurfaceManagerFactory. + * The SurfaceManagerFactory that creates VolatileSurfaceManager + * implementations for the Unix volatile images. */ -public class SurfaceManagerFactory { +public class UnixSurfaceManagerFactory extends SurfaceManagerFactory { + /** * Creates a new instance of a VolatileSurfaceManager given any * arbitrary SunVolatileImage. An optional context Object can be supplied @@ -51,9 +50,8 @@ public class SurfaceManagerFactory { * specific VolatileSurfaceManager based on the GraphicsConfiguration * under which the SunVolatileImage was created. */ - public static VolatileSurfaceManager - createVolatileManager(SunVolatileImage vImg, - Object context) + public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg, + Object context) { GraphicsConfiguration gc = vImg.getGraphicsConfig(); if (gc instanceof GLXGraphicsConfig) { @@ -62,4 +60,5 @@ public class SurfaceManagerFactory { return new X11VolatileSurfaceManager(vImg, context); } } + } diff --git a/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java b/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java index 850733e242f..c3820c22a5f 100644 --- a/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java +++ b/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java @@ -42,6 +42,8 @@ import sun.awt.windows.WPrinterJob; import sun.awt.windows.WToolkit; import sun.font.FontManager; import sun.java2d.SunGraphicsEnvironment; +import sun.java2d.SurfaceManagerFactory; +import sun.java2d.WindowsSurfaceManagerFactory; import sun.java2d.windows.WindowsFlags; /** @@ -64,6 +66,9 @@ public class Win32GraphicsEnvironment WindowsFlags.initFlags(); initDisplayWrapper(); eudcFontFileName = getEUDCFontFile(); + + // Install correct surface manager factory. + SurfaceManagerFactory.setInstance(new WindowsSurfaceManagerFactory()); } /** diff --git a/jdk/src/windows/classes/sun/java2d/SurfaceManagerFactory.java b/jdk/src/windows/classes/sun/java2d/WindowsSurfaceManagerFactory.java similarity index 83% rename from jdk/src/windows/classes/sun/java2d/SurfaceManagerFactory.java rename to jdk/src/windows/classes/sun/java2d/WindowsSurfaceManagerFactory.java index a1e16ea03cb..af0fd7bd400 100644 --- a/jdk/src/windows/classes/sun/java2d/SurfaceManagerFactory.java +++ b/jdk/src/windows/classes/sun/java2d/WindowsSurfaceManagerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2008 Sun Microsystems, Inc. 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 @@ -36,11 +36,11 @@ import sun.java2d.windows.WindowsFlags; import sun.java2d.windows.WinVolatileSurfaceManager; /** - * This is a factory class with static methods for creating a - * platform-specific instance of a particular SurfaceManager. Each platform - * (Windows, Unix, etc.) has its own specialized SurfaceManagerFactory. + * The SurfaceManagerFactory that creates VolatileSurfaceManager + * implementations for the Windows volatile images. */ -public class SurfaceManagerFactory { +public class WindowsSurfaceManagerFactory extends SurfaceManagerFactory { + /** * Creates a new instance of a VolatileSurfaceManager given any * arbitrary SunVolatileImage. An optional context Object can be supplied @@ -50,9 +50,8 @@ public class SurfaceManagerFactory { * For Windows platforms, this method returns a Windows-specific * VolatileSurfaceManager. */ - public static VolatileSurfaceManager - createVolatileManager(SunVolatileImage vImg, - Object context) + public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg, + Object context) { GraphicsConfiguration gc = vImg.getGraphicsConfig(); if (gc instanceof WGLGraphicsConfig) { @@ -61,4 +60,5 @@ public class SurfaceManagerFactory { return new WinVolatileSurfaceManager(vImg, context); } } + } From 69a10f680ae409a90d10beace5f7cc3d254b0d32 Mon Sep 17 00:00:00 2001 From: Oleg Sukhodolsky Date: Thu, 15 May 2008 11:34:11 +0400 Subject: [PATCH 22/40] 6644301: lightweight components can repaint outside request bounds Repaint() needs to adjust width and height if it receives negative x or y. Reviewed-by: art --- jdk/src/share/classes/java/awt/Component.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/java/awt/Component.java b/jdk/src/share/classes/java/awt/Component.java index 22fb95f5aa9..aa9f593127e 100644 --- a/jdk/src/share/classes/java/awt/Component.java +++ b/jdk/src/share/classes/java/awt/Component.java @@ -3052,10 +3052,24 @@ public abstract class Component implements ImageObserver, MenuContainer, // services. Additionally, the request is restricted to // the bounds of the component. if (parent != null) { - int px = this.x + ((x < 0) ? 0 : x); - int py = this.y + ((y < 0) ? 0 : y); + if (x < 0) { + width += x; + x = 0; + } + if (y < 0) { + height += y; + y = 0; + } + int pwidth = (width > this.width) ? this.width : width; int pheight = (height > this.height) ? this.height : height; + + if (pwidth <= 0 || pheight <= 0) { + return; + } + + int px = this.x + x; + int py = this.y + y; parent.repaint(tm, px, py, pwidth, pheight); } } else { From a83943f8b04219ba2667061b1e6dd13510676ff8 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Thu, 15 May 2008 10:26:07 +0100 Subject: [PATCH 23/40] 6670408: testcase panics 1.5.0_12&_14 JVM when java.net.PlainSocketImpl trying to throw an exception Replace select with poll Reviewed-by: alanb, jccollet --- .../solaris/native/java/net/PlainSocketImpl.c | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/jdk/src/solaris/native/java/net/PlainSocketImpl.c b/jdk/src/solaris/native/java/net/PlainSocketImpl.c index 2490dbb0b1b..3fcaa9ee5d0 100644 --- a/jdk/src/solaris/native/java/net/PlainSocketImpl.c +++ b/jdk/src/solaris/native/java/net/PlainSocketImpl.c @@ -358,15 +358,28 @@ Java_java_net_PlainSocketImpl_socketConnect(JNIEnv *env, jobject this, * See 6343810. */ while (1) { - fd_set wr, ex; +#ifndef USE_SELECT + { +fprintf(stdout,"\nNATIVE: fd = %d] ", fd); + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLOUT; - FD_ZERO(&wr); - FD_SET(fd, &wr); - FD_ZERO(&ex); - FD_SET(fd, &ex); + connect_rv = NET_Poll(&pfd, 1, -1); + } +#else + { + fd_set wr, ex; + + FD_ZERO(&wr); + FD_SET(fd, &wr); + FD_ZERO(&ex); + FD_SET(fd, &ex); + + connect_rv = NET_Select(fd+1, 0, &wr, &ex, 0); + } +#endif - errno = 0; - connect_rv = NET_Select(fd+1, 0, &wr, &ex, 0); if (connect_rv == JVM_IO_ERR) { if (errno == EINTR) { continue; From 1138f00d745f26fb5c5b576ad892ddaf1ed51a0e Mon Sep 17 00:00:00 2001 From: Igor Nekrestyanov Date: Fri, 16 May 2008 03:10:58 +0400 Subject: [PATCH 24/40] 6630501: CRASH: JCK test eats much memory and jvm crashes Reviewed-by: bae, prr --- jdk/src/share/classes/sun/font/Type1Font.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/share/classes/sun/font/Type1Font.java b/jdk/src/share/classes/sun/font/Type1Font.java index a3b0cda3c86..90236ff64c1 100644 --- a/jdk/src/share/classes/sun/font/Type1Font.java +++ b/jdk/src/share/classes/sun/font/Type1Font.java @@ -589,7 +589,7 @@ public class Type1Font extends FileFont { protected synchronized FontScaler getScaler() { if (scaler == null) { - return FontManager.getScaler(this, 0, false, fileSize); + scaler = FontManager.getScaler(this, 0, false, fileSize); } return scaler; From be8dde580e86b2252ecfae50ead3a59e413e4c77 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 19 May 2008 11:25:32 -0700 Subject: [PATCH 25/40] 6611637: NullPointerException in sun.font.GlyphLayout$EngineRecord.init Reviewed-by: tdv, jgodinez --- jdk/src/share/classes/sun/font/GlyphLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/share/classes/sun/font/GlyphLayout.java b/jdk/src/share/classes/sun/font/GlyphLayout.java index d34734f8e9a..468e4a118c5 100644 --- a/jdk/src/share/classes/sun/font/GlyphLayout.java +++ b/jdk/src/share/classes/sun/font/GlyphLayout.java @@ -85,7 +85,7 @@ public final class GlyphLayout { private GVData _gvdata; // cached glyph layout data for reuse - private static GlyphLayout cache; // reusable + private static volatile GlyphLayout cache; // reusable private LayoutEngineFactory _lef; // set when get is called, unset when done is called private TextRecord _textRecord; // the text we're working on, used by iterators From 4b9a053b3fa4ab2e0c01f5525b6e3551f32f1a86 Mon Sep 17 00:00:00 2001 From: Yuri Nesterenko Date: Wed, 21 May 2008 10:28:19 +0400 Subject: [PATCH 26/40] 6253172: Some key characters on none US keyboard cannot be typed since JDK 1.4 Windows-only problem fixed by applying 4737679/4623376 fix to navigation keys only. Reviewed-by: son --- .../native/sun/windows/awt_Component.cpp | 20 ++++++++++++++++++- .../native/sun/windows/awt_Component.h | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/jdk/src/windows/native/sun/windows/awt_Component.cpp b/jdk/src/windows/native/sun/windows/awt_Component.cpp index cc0d3569dc8..ea9ce00f0aa 100644 --- a/jdk/src/windows/native/sun/windows/awt_Component.cpp +++ b/jdk/src/windows/native/sun/windows/awt_Component.cpp @@ -3464,6 +3464,21 @@ UINT AwtComponent::WindowsKeyToJavaKey(UINT windowsKey, UINT modifiers) return java_awt_event_KeyEvent_VK_UNDEFINED; } +BOOL AwtComponent::IsNavigationKey(UINT wkey) { + switch (wkey) { + case VK_END: + case VK_PRIOR: // PageUp + case VK_NEXT: // PageDown + case VK_HOME: + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + return TRUE; + } + return FALSE; +} + // determine if a key is a numpad key (distinguishes the numpad // arrow keys from the non-numpad arrow keys, for example). BOOL AwtComponent::IsNumPadKey(UINT vkey, BOOL extended) @@ -3563,7 +3578,10 @@ UINT AwtComponent::WindowsKeyToJavaChar(UINT wkey, UINT modifiers, TransOps ops) // fix for 4623376,4737679,4501485,4740906,4708221 (4173679/4122715) // Here we try to resolve a conflict with ::ToAsciiEx's translating // ALT+number key combinations. kdm@sarc.spb.su - keyboardState[VK_MENU] &= ~KEY_STATE_DOWN; + // yan: Do it for navigation keys only, otherwise some AltGr deadkeys fail. + if( IsNavigationKey(wkey) ) { + keyboardState[VK_MENU] &= ~KEY_STATE_DOWN; + } if (ctrlIsDown) { diff --git a/jdk/src/windows/native/sun/windows/awt_Component.h b/jdk/src/windows/native/sun/windows/awt_Component.h index ea3bd2066f6..b4bd849424c 100644 --- a/jdk/src/windows/native/sun/windows/awt_Component.h +++ b/jdk/src/windows/native/sun/windows/awt_Component.h @@ -823,6 +823,7 @@ public: private: AwtComponent* SearchChild(UINT id); void RemoveChild(UINT id) ; + static BOOL IsNavigationKey(UINT wkey); ChildListItem* m_childList; From 68458b3b27a1f8adae99ef48b4463fd75b8f150b Mon Sep 17 00:00:00 2001 From: Igor Nekrestyanov Date: Wed, 21 May 2008 10:59:07 +0400 Subject: [PATCH 27/40] 6703377: freetype: glyph vector outline is not translated correctly Reviewed-by: bae, prr --- .../share/native/sun/font/freetypeScaler.c | 2 +- .../font/Rotate/TranslatedOutlineTest.java | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 jdk/test/java/awt/font/Rotate/TranslatedOutlineTest.java diff --git a/jdk/src/share/native/sun/font/freetypeScaler.c b/jdk/src/share/native/sun/font/freetypeScaler.c index 051e7c4c4a0..a3229df5dd1 100644 --- a/jdk/src/share/native/sun/font/freetypeScaler.c +++ b/jdk/src/share/native/sun/font/freetypeScaler.c @@ -985,7 +985,7 @@ static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D, FT_Outline_Translate(&ftglyph->outline, FloatToF26Dot6(xpos), - FloatToF26Dot6(ypos)); + -FloatToF26Dot6(ypos)); return &ftglyph->outline; } diff --git a/jdk/test/java/awt/font/Rotate/TranslatedOutlineTest.java b/jdk/test/java/awt/font/Rotate/TranslatedOutlineTest.java new file mode 100644 index 00000000000..d94b3753515 --- /dev/null +++ b/jdk/test/java/awt/font/Rotate/TranslatedOutlineTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test TranslatedOutlineTest + * @bug 6703377 + * @summary This test verifies that outline is translated in a correct direction + * @run main TranslatedOutlineTest + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.image.BufferedImage; + +public class TranslatedOutlineTest { + public static void main(String a[]) { + /* prepare blank image */ + BufferedImage bi = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = (Graphics2D) bi.getGraphics(); + g2.setColor(Color.WHITE); + g2.fillRect(0, 0, 50, 50); + + /* draw outline somethere in the middle of the image */ + FontRenderContext frc = new FontRenderContext(null, false, false); + g2.setColor(Color.RED); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + GlyphVector gv = g2.getFont().createGlyphVector(frc, "test"); + g2.fill(gv.getOutline(20, 20)); + + /* Check if anything was drawn. + * If y direction is not correct then image is still blank and + * test will fail. + */ + int bgcolor = Color.WHITE.getRGB(); + for (int i=0; i Date: Thu, 22 May 2008 09:37:37 -0700 Subject: [PATCH 28/40] Added tag jdk7-b27 for changeset fcdebcbc4177 --- .hgtags-top-repo | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags-top-repo b/.hgtags-top-repo index 6ed900e384c..2d38b379a00 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -1,3 +1,4 @@ cfeea66a3fa8ca3686a7cfa2d0ce8ab0169f168d jdk7-b24 cbc8ad9dd0e085a607427ea35411990982f19a36 jdk7-b25 9410f77cc30c604d1caf7c9fe3a57fa19e1acbe8 jdk7-b26 +11b4dc9f2be3523ef989a0db8459eb56b3045c3a jdk7-b27 From 9f5e04c259bd5f35db43172a47e85a0773fb5243 Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 22 May 2008 09:37:38 -0700 Subject: [PATCH 29/40] Added tag jdk7-b27 for changeset 4d94f3a46d3e --- corba/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/corba/.hgtags b/corba/.hgtags index 3832215a78c..1c9eb9a6072 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -1,3 +1,4 @@ 55540e827aef970ecc010b7e06b912d991c8e3ce jdk7-b24 5e61d5df62586474414d1058e9186441aa908f51 jdk7-b25 0043eb3d4e628f049ff80a8c223b5657136085e7 jdk7-b26 +e84e9018bebbf3e5bafc5706e7882a15cb1c7d99 jdk7-b27 From 6346f4b62bce90be4e17cdb7ff7db6392cc88f27 Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 22 May 2008 09:37:40 -0700 Subject: [PATCH 30/40] Added tag jdk7-b27 for changeset 9ba36c07dcee --- hotspot/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/.hgtags b/hotspot/.hgtags index f3431f3fa42..5d8c9da580d 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -1,3 +1,4 @@ a61af66fc99eb5ec9d50c05b0c599757b1289ceb jdk7-b24 7836be3e92d0a4f9ee7566f602c91f5609534e66 jdk7-b25 ad0b851458ff9d1d490ed2d79bb84f75a9fdb753 jdk7-b26 +e3d2692f8442e2d951166dc9bd9a330684754438 jdk7-b27 From 5e5fa1008324b8880a7524275a74a48f1a165eb7 Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 22 May 2008 09:37:44 -0700 Subject: [PATCH 31/40] Added tag jdk7-b27 for changeset 907e5ecf4714 --- jaxp/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxp/.hgtags b/jaxp/.hgtags index 20603a69752..63a4b81940f 100644 --- a/jaxp/.hgtags +++ b/jaxp/.hgtags @@ -1,3 +1,4 @@ 6ce5f4757bde08f7470cbb9f0b46da8f2f3d4f56 jdk7-b24 a3b3ba7d6034dc754b51ddc3d281399ac1cae5f1 jdk7-b25 da43cb85fac1646d6f97e4a35e510bbfdff97bdb jdk7-b26 +bafed478d67c3acf7744aaad88b9404261ea6739 jdk7-b27 From a9926f22874a1664febd8b49dae7d24b02844a91 Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 22 May 2008 09:37:45 -0700 Subject: [PATCH 32/40] Added tag jdk7-b27 for changeset 2c73948e4bf3 --- jaxws/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxws/.hgtags b/jaxws/.hgtags index 05a30918f4e..95df1d8c5f8 100644 --- a/jaxws/.hgtags +++ b/jaxws/.hgtags @@ -1,3 +1,4 @@ 0961a4a211765fea071b8dac419003ee0c3d5973 jdk7-b24 59fd8224ba2da5c2d8d4c68e33cf33ab41ce8de0 jdk7-b25 debd37e1a422e580edb086c95d6e89199133a39c jdk7-b26 +27d8f42862c11b4ddc4af2dd2d2a3cd86cda04c2 jdk7-b27 From 2bd5006eac88ae3fd40534dfecd63ce496df3511 Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Thu, 22 May 2008 09:37:57 -0700 Subject: [PATCH 33/40] Added tag jdk7-b27 for changeset ae44a6d84438 --- langtools/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/langtools/.hgtags b/langtools/.hgtags index 04f4474ee22..ebaa7fa9637 100644 --- a/langtools/.hgtags +++ b/langtools/.hgtags @@ -1,3 +1,4 @@ 9a66ca7c79fab293c1bb0534e0d208c7e4f58b01 jdk7-b24 58039502942e52f4144a33f36290a2bd2f3581e6 jdk7-b25 c46d25a2350ac147d0121d9c9725af6fcb1b4dbe jdk7-b26 +a17265993253d61becd04fe7d96d1fe8b4bd6dff jdk7-b27 From e072682162f8ac1d6f5824cde7b9b6baf14c51f6 Mon Sep 17 00:00:00 2001 From: Bradford Wetmore Date: Thu, 22 May 2008 14:20:53 -0700 Subject: [PATCH 34/40] 6706358: jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java has the wrong copyright notice Reviewed-by: valeriep --- .../pkcs11/Cipher/TestSymmCiphers.java | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java index e102606b4d1..5b1e6de3737 100644 --- a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java +++ b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java @@ -2,32 +2,22 @@ * Copyright 2008 Sun Microsystems, Inc. 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 modi -fy it - * under the terms of the GNU General Public License version 2 onl -y, as + * 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. * - * This code is distributed in the hope that it will be useful, bu -t WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABIL -ITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public L -icense - * version 2 for more details (a copy is included in the LICENSE f -ile that + * 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 Licen -se version - * 2 along with this work; if not, write to the Free Software Foun -dation, + * 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 Sun Microsystems, Inc., 4150 Network Circle, San -ta Clara, - * CA 95054 USA or visit www.sun.com if you need additional inform -ation or + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ From c0dfc6ea2192d64250756ff855026e31b6eefcaf Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Tue, 27 May 2008 14:29:32 +0800 Subject: [PATCH 35/40] 6705313: Incorrect exit $? in keytool's autotest.sh Reviewed-by: valeriep --- jdk/test/sun/security/tools/keytool/autotest.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jdk/test/sun/security/tools/keytool/autotest.sh b/jdk/test/sun/security/tools/keytool/autotest.sh index 5376b257a4e..04c00c14ebf 100644 --- a/jdk/test/sun/security/tools/keytool/autotest.sh +++ b/jdk/test/sun/security/tools/keytool/autotest.sh @@ -1,5 +1,5 @@ # -# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2006-2008 Sun Microsystems, Inc. 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 @@ -90,7 +90,8 @@ chmod u+w cert8.db echo | ${TESTJAVA}${FS}bin${FS}java -Dfile -Dnss \ -Dnss.lib=${NSS}${FS}lib${FS}${PF}${FS}${LIBNAME} \ - KeyToolTest || exit 12 + KeyToolTest +status=$? rm -f p11-nss.txt rm -f cert8.db @@ -101,4 +102,5 @@ rm HumanInputStream*.class rm KeyToolTest.class rm TestException.class -exit $? +exit $status + From da21254012aa6ac7ee29a1e4610c37b28b268a3b Mon Sep 17 00:00:00 2001 From: Andrei Dmitriev Date: Thu, 29 May 2008 13:48:51 +0400 Subject: [PATCH 36/40] 6691328: DragSourceContext returns unexpected cursor Make the code to be executed if other options don't suit Reviewed-by: dcherepanov --- jdk/src/share/classes/java/awt/dnd/DragSourceContext.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/java/awt/dnd/DragSourceContext.java b/jdk/src/share/classes/java/awt/dnd/DragSourceContext.java index 050ab6d5826..89d0619b063 100644 --- a/jdk/src/share/classes/java/awt/dnd/DragSourceContext.java +++ b/jdk/src/share/classes/java/awt/dnd/DragSourceContext.java @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. 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 @@ -485,7 +485,6 @@ public class DragSourceContext Cursor c = null; - targetAct = DnDConstants.ACTION_NONE; switch (status) { case ENTER: case OVER: @@ -507,6 +506,10 @@ public class DragSourceContext else c = DragSource.DefaultCopyDrop; } + break; + default: + targetAct = DnDConstants.ACTION_NONE; + } setCursorImpl(c); From eaa732b2fd80bc36757987f682fa3db38fc3f91e Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 29 May 2008 15:33:14 +0200 Subject: [PATCH 37/40] 6673853: LegacyIntrospectorTest is testing an old deprecated com.sun API not present in OpenJDK Removed test from open test suite - the corresponding deprecated legacy API is not in open source tree Reviewed-by: emcmanus --- .../Introspector/LegacyIntrospectorTest.java | 75 ------------------- 1 file changed, 75 deletions(-) delete mode 100644 jdk/test/javax/management/Introspector/LegacyIntrospectorTest.java diff --git a/jdk/test/javax/management/Introspector/LegacyIntrospectorTest.java b/jdk/test/javax/management/Introspector/LegacyIntrospectorTest.java deleted file mode 100644 index 19499dde08a..00000000000 --- a/jdk/test/javax/management/Introspector/LegacyIntrospectorTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2005 Sun Microsystems, Inc. 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. - * - * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -/* - * @test - * @bug 6316460 - * @summary Test that the legacy com.sun.management.jmx.Introspector - * methods work. - * @author Eamonn McManus - * @run clean LegacyIntrospectorTest - * @run build LegacyIntrospectorTest - * @run main LegacyIntrospectorTest - */ - -import javax.management.*; -import com.sun.management.jmx.*; - -public class LegacyIntrospectorTest { - public static interface TestMBean { - public int getWhatever(); - } - public static class Test implements TestMBean { - public int getWhatever() {return 0;} - } - - @SuppressWarnings("deprecation") - public static void main(String[] args) throws Exception { - MBeanInfo mbi = Introspector.testCompliance(Test.class); - MBeanAttributeInfo mbai = mbi.getAttributes()[0]; - if (!mbai.getName().equals("Whatever")) - throw new Exception("Wrong attribute name: " + mbai.getName()); - Class c = Introspector.getMBeanInterface(Test.class); - if (c != TestMBean.class) - throw new Exception("Wrong interface: " + c); - - MBeanServer mbs1 = new MBeanServerImpl(); - if (!mbs1.getDefaultDomain().equals("DefaultDomain")) - throw new Exception("Wrong default domain: " + mbs1.getDefaultDomain()); - - MBeanServer mbs2 = new MBeanServerImpl("Foo"); - if (!mbs2.getDefaultDomain().equals("Foo")) - throw new Exception("Wrong default domain: " + mbs2.getDefaultDomain()); - - ObjectName delegateName = - new ObjectName("JMImplementation:type=MBeanServerDelegate"); - MBeanInfo delegateInfo = mbs2.getMBeanInfo(delegateName); - MBeanInfo refDelegateInfo = - MBeanServerFactory.newMBeanServer().getMBeanInfo(delegateName); - if (!delegateInfo.equals(refDelegateInfo)) - throw new Exception("Wrong delegate info from MBeanServerImpl: " + - delegateInfo); - - System.out.println("TEST PASSED"); - } -} From 160c6f164decc521ea6516edf8110d232451984e Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Fri, 30 May 2008 14:35:43 +0200 Subject: [PATCH 38/40] 6592586: RequiredModelMBean prints a WARNING message when calling getAttributes() for a non-existing attr Switched traces to FINER - except when logging fails - in which cases the traces are logged to FINE Reviewed-by: emcmanus --- .../modelmbean/RequiredModelMBean.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/jdk/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java b/jdk/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java index 86ce9d904b0..6a72302ea20 100644 --- a/jdk/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java +++ b/jdk/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java @@ -1696,8 +1696,8 @@ public class RequiredModelMBean } catch (Exception e) { // eat exceptions because interface doesn't have an // exception on it - if (MODELMBEAN_LOGGER.isLoggable(Level.WARNING)) { - MODELMBEAN_LOGGER.logp(Level.WARNING, + if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { + MODELMBEAN_LOGGER.logp(Level.FINER, RequiredModelMBean.class.getName(), "getAttributes(String[])", "Failed to get \"" + attrNames[i] + "\": ", e); @@ -1857,8 +1857,8 @@ public class RequiredModelMBean attrValue.getClass().getName() + " received."); } catch (ClassNotFoundException x) { - if (MODELMBEAN_LOGGER.isLoggable(Level.WARNING)) { - MODELMBEAN_LOGGER.logp(Level.WARNING, + if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { + MODELMBEAN_LOGGER.logp(Level.FINER, RequiredModelMBean.class.getName(), "setAttribute(Attribute)","Class " + attrType + " for attribute " @@ -2224,8 +2224,8 @@ public class RequiredModelMBean ntfyObj.getMessage() + " Severity = " + (String)ntfyDesc.getFieldValue("severity")); } catch (Exception e) { - if (MODELMBEAN_LOGGER.isLoggable(Level.WARNING)) { - MODELMBEAN_LOGGER.logp(Level.WARNING, + if (MODELMBEAN_LOGGER.isLoggable(Level.FINE)) { + MODELMBEAN_LOGGER.logp(Level.FINE, RequiredModelMBean.class.getName(), "sendNotification(Notification)", "Failed to log " + @@ -2618,8 +2618,8 @@ public class RequiredModelMBean " Old value = " + oldv + " New value = " + newv); } catch (Exception e) { - if (MODELMBEAN_LOGGER.isLoggable(Level.WARNING)) { - MODELMBEAN_LOGGER.logp(Level.WARNING, + if (MODELMBEAN_LOGGER.isLoggable(Level.FINE)) { + MODELMBEAN_LOGGER.logp(Level.FINE, RequiredModelMBean.class.getName(),mth, "Failed to log " + ntfyObj.getType() + " notification: ", e); @@ -2644,8 +2644,8 @@ public class RequiredModelMBean " Old value = " + oldv + " New value = " + newv); } catch (Exception e) { - if (MODELMBEAN_LOGGER.isLoggable(Level.WARNING)) { - MODELMBEAN_LOGGER.logp(Level.WARNING, + if (MODELMBEAN_LOGGER.isLoggable(Level.FINE)) { + MODELMBEAN_LOGGER.logp(Level.FINE, RequiredModelMBean.class.getName(),mth, "Failed to log " + ntfyObj.getType() + " notification: ", e); From 8193c1374898ddc2016bcdb798090728b7e99250 Mon Sep 17 00:00:00 2001 From: "J. Duke" Date: Wed, 5 Jul 2017 16:36:55 +0200 Subject: [PATCH 39/40] Added tag jdk7-b27 for changeset 67052ac87fc9 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 2163563fbc4..a9925795d56 100644 --- a/.hgtags +++ b/.hgtags @@ -1,3 +1,4 @@ 1cc8dd79fd1cd13d36b385196271a29632c67c3b jdk7-b24 bf2517e15f0c0f950e5b3143c4ca11e2df73dcc1 jdk7-b25 5ae7db536e3fcf6be78e45b240a9058095e0ed38 jdk7-b26 +67052ac87fc927d048e62ec54ff42adb230d3f7c jdk7-b27 From eca36c6c047a74462c3e68a756a84ce85aff3035 Mon Sep 17 00:00:00 2001 From: Xiomara Jayasena Date: Tue, 10 Jun 2008 10:33:27 -0700 Subject: [PATCH 40/40] Added tag jdk7-b28 for changeset 2063bcfb0878 --- jdk/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/.hgtags b/jdk/.hgtags index 6f79b84210f..940e5c1079d 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -2,3 +2,4 @@ 75fca0b0ab83ab1392e615910cea020f66535390 jdk7-b25 fb57027902e04ecafceae31a605e69b436c23d57 jdk7-b26 3e599d98875ddf919c8ea11cff9b3a99ba631a9b jdk7-b27 +02e4c5348592a8d7fc2cba28bc5f8e35c0e17277 jdk7-b28