Source Code Cross Referenced for IIPCRIF.java in  » 6.0-JDK-Modules » Java-Advanced-Imaging » com » sun » media » jai » opimage » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » Java Advanced Imaging » com.sun.media.jai.opimage 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $RCSfile: IIPCRIF.java,v $
0003:         *
0004:         * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * Use is subject to license terms.
0007:         *
0008:         * $Revision: 1.1 $
0009:         * $Date: 2005/02/11 04:56:28 $
0010:         * $State: Exp $
0011:         */
0012:        package com.sun.media.jai.opimage;
0013:
0014:        import java.awt.Image;
0015:        import java.awt.Rectangle;
0016:        import java.awt.RenderingHints;
0017:        import java.awt.Shape;
0018:        import java.awt.color.ColorSpace;
0019:        import java.awt.geom.AffineTransform;
0020:        import java.awt.geom.Point2D;
0021:        import java.awt.geom.Rectangle2D;
0022:        import java.awt.image.ColorModel;
0023:        import java.awt.image.ComponentColorModel;
0024:        import java.awt.image.RenderedImage;
0025:        import java.awt.image.renderable.ParameterBlock;
0026:        import java.awt.image.renderable.RenderableImage;
0027:        import java.awt.image.renderable.RenderableImageOp;
0028:        import java.awt.image.renderable.RenderContext;
0029:        import java.io.InputStream;
0030:        import java.net.URL;
0031:        import java.util.Vector;
0032:        import javax.media.jai.CRIFImpl;
0033:        import javax.media.jai.EnumeratedParameter;
0034:        import javax.media.jai.ImageLayout;
0035:        import javax.media.jai.Interpolation;
0036:        import javax.media.jai.JAI;
0037:        import javax.media.jai.LookupTableJAI;
0038:        import javax.media.jai.MultiResolutionRenderableImage;
0039:        import javax.media.jai.PlanarImage;
0040:        import javax.media.jai.ROI;
0041:        import javax.media.jai.ROIShape;
0042:        import javax.media.jai.TiledImage;
0043:        import javax.media.jai.operator.TransposeDescriptor;
0044:        import javax.media.jai.util.ImagingException;
0045:        import javax.media.jai.util.ImagingListener;
0046:        import com.sun.media.jai.codec.ImageCodec;
0047:        import com.sun.media.jai.codec.ImageDecoder;
0048:        import com.sun.media.jai.codec.MemoryCacheSeekableStream;
0049:        import com.sun.media.jai.util.ImageUtil;
0050:
0051:        /**
0052:         * This CRIF implements the "iip" operation in the rendered and renderable
0053:         * image layers.
0054:         *
0055:         * <p> In renderable mode this operation is designed to execute on the
0056:         * server as many composed operations (those specified via parameters) as
0057:         * the server's capability permits.  In the 1.0 implementation all operations
0058:         * are actually carried out on the client: server-side processing will be
0059:         * added in a subsequent release.
0060:         *
0061:         * <p> Rendered operation returns the default rendering of renderable mode
0062:         * operation.
0063:         *
0064:         * <p> The actual set of composed operations is described in section 2.2.1.1
0065:         * "Composed Image Commands" ot the "Internet Imaging Protocol Specification"
0066:         * version 1.0.5.  The sequence in which these commands are to be applied is
0067:         * also described in this section.
0068:         *
0069:         * <p> More detailed information actually required to understand and implement
0070:         * the composed operations is available in the "FlashPix Format Specification"
0071:         * version 1.0.1.  Of particular interest are Section 2 "Image Data
0072:         * Representation", Section 5.3.3 "Relationship of NIF RGB to sRGB",
0073:         * Section 5.4 "Relating PhotoYCC to NIG RGB", Section 7.2 "Viewing Transform
0074:         * Parameters", and Section 7.3 "Sequence of Viewing Parameter
0075:         * Transformations".  Also of note are Tables 3.6 and 3.7.
0076:         *
0077:         * @since 1.0
0078:         *
0079:         * @see IIPDescriptor
0080:         * @see IIPResolutionRIF
0081:         * @see IIPResolutionOpImage
0082:         * @see <a href="http://www.digitalimaging.org">Digital Imaging Group</a>
0083:         *
0084:         */
0085:        public class IIPCRIF extends CRIFImpl {
0086:            // Bitmask constants indicating supplied parameters.
0087:            private static final int MASK_FILTER = 0x1;
0088:            private static final int MASK_COLOR_TWIST = 0x2;
0089:            private static final int MASK_CONTRAST = 0x4;
0090:            private static final int MASK_ROI_SOURCE = 0x8;
0091:            private static final int MASK_TRANSFORM = 0x10;
0092:            private static final int MASK_ASPECT_RATIO = 0x20;
0093:            private static final int MASK_ROI_DESTINATION = 0x40;
0094:            private static final int MASK_ROTATION = 0x80;
0095:            private static final int MASK_MIRROR_AXIS = 0x100;
0096:            private static final int MASK_ICC_PROFILE = 0x200;
0097:            private static final int MASK_JPEG_QUALITY = 0x400;
0098:            private static final int MASK_JPEG_TABLE = 0x800;
0099:
0100:            // Constants indicating server vendors.
0101:            private static final int VENDOR_HP = 0;
0102:            private static final int VENDOR_LIVE_PICTURE = 1;
0103:            private static final int VENDOR_KODAK = 2;
0104:            private static final int VENDOR_UNREGISTERED = 255;
0105:            private static final int VENDOR_EXPERIMENTAL = 999;
0106:
0107:            //  Bitmask constants indicating server capabilities
0108:            private static final int SERVER_CVT_JPEG = 0x1;
0109:            private static final int SERVER_CVT_FPX = 0x2;
0110:            private static final int SERVER_CVT_MJPEG = 0x4;
0111:            private static final int SERVER_CVT_MFPX = 0x8;
0112:            private static final int SERVER_CVT_M2JPEG = 0x10;
0113:            private static final int SERVER_CVT_M2FPX = 0x20;
0114:            private static final int SERVER_CVT_JTL = 0x40;
0115:
0116:            // Special bitmask combinations
0117:            private static final int SERVER_JPEG_PARTIAL = SERVER_CVT_JPEG
0118:                    | SERVER_CVT_MJPEG;
0119:            private static final int SERVER_JPEG_FULL = SERVER_JPEG_PARTIAL
0120:                    | SERVER_CVT_M2JPEG;
0121:            private static final int SERVER_FPX_PARTIAL = SERVER_CVT_FPX
0122:                    | SERVER_CVT_MFPX;
0123:            private static final int SERVER_FPX_FULL = SERVER_FPX_PARTIAL
0124:                    | SERVER_CVT_M2FPX;
0125:
0126:            // --- RGB[A] <-> PhotoYCC[A] metric conversion matrices ---
0127:            // As stated in the FlashPix specification, these matrices are
0128:            // sufficient for tone and color correction but should not be
0129:            // used for actual color space conversion calculations.
0130:
0131:            // PhotoYCCA -> RGBA metric color conversion matrix
0132:            private static final double[][] YCCA_TO_RGBA = new double[][] {
0133:                    { 1.358400, 0.000000, 1.821500, 0.000000 },
0134:                    { 1.358400, -0.430300, -0.927100, 0.000000 },
0135:                    { 1.358400, 2.217900, 0.000000, 0.000000 },
0136:                    { 0.000000, 0.000000, 0.000000, 1.000000 } };
0137:
0138:            // PhotoYCCA -> RGBA metric color conversion constant
0139:            private static final double[][] YCCA_TO_RGBA_CONST = new double[][] {
0140:                    { -249.55 }, { 194.14 }, { -345.99 }, { 0.0 } };
0141:
0142:            // RGBA -> PhotoYCCA metric color conversion matrix
0143:            private static final double[][] RGBA_TO_YCCA = new double[][] {
0144:                    { 0.220018, 0.432276, 0.083867, 0.000000 },
0145:                    { -0.134755, -0.264756, 0.399511, 0.000000 },
0146:                    { 0.384918, -0.322373, -0.062544, 0.000000 },
0147:                    { 0.000000, 0.000000, 0.000000, 1.000000 } };
0148:
0149:            // RGBA -> PhotoYCCA metric color conversion constant
0150:            private static final double[][] RGBA_TO_YCCA_CONST = new double[][] {
0151:                    { 0.0005726 }, { 155.9984 }, { 137.0022 }, { 0.0 } };
0152:
0153:            // PhotoYCC -> RGB metric color conversion matrix
0154:            private static final double[][] YCC_TO_RGB = new double[][] {
0155:                    { 1.358400, 0.000000, 1.821500 },
0156:                    { 1.358400, -0.430300, -0.927100 },
0157:                    { 1.358400, 2.217900, 0.000000 } };
0158:
0159:            // PhotoYCC -> RGB metric color conversion constant
0160:            private static final double[][] YCC_TO_RGB_CONST = new double[][] {
0161:                    { -249.55 }, { 194.14 }, { -345.99 } };
0162:
0163:            // RGB -> PhotoYCC metric color conversion matrix
0164:            private static final double[][] RGB_TO_YCC = new double[][] {
0165:                    { 0.220018, 0.432276, 0.083867 },
0166:                    { -0.134755, -0.264756, 0.399511 },
0167:                    { 0.384918, -0.322373, -0.062544 } };
0168:
0169:            // RGB -> PhotoYCC metric color conversion constant
0170:            private static final double[][] RGB_TO_YCC_CONST = new double[][] {
0171:                    { 0.0005726 }, { 155.9984 }, { 137.0022 } };
0172:
0173:            /**
0174:             * Returns the operation mask based on the supplied parameters.
0175:             */
0176:            private static final int getOperationMask(ParameterBlock pb) {
0177:                int opMask = 0;
0178:
0179:                // Initialize the operation mask according to which
0180:                // parameters are actually supplied.
0181:                if (pb.getFloatParameter(2) != 0.0F) {
0182:                    opMask |= MASK_FILTER;
0183:                }
0184:                if (pb.getObjectParameter(3) != null) {
0185:                    opMask |= MASK_COLOR_TWIST;
0186:                }
0187:                if (Math.abs(pb.getFloatParameter(4) - 1.0F) > 0.01F) {
0188:                    opMask |= MASK_CONTRAST;
0189:                }
0190:                if (pb.getObjectParameter(5) != null) {
0191:                    opMask |= MASK_ROI_SOURCE;
0192:                }
0193:                AffineTransform tf = (AffineTransform) pb.getObjectParameter(6);
0194:                if (!tf.isIdentity()) {
0195:                    opMask |= MASK_TRANSFORM;
0196:                }
0197:                if (pb.getObjectParameter(7) != null) {
0198:                    opMask |= MASK_ASPECT_RATIO;
0199:                }
0200:                if (pb.getObjectParameter(8) != null) {
0201:                    opMask |= MASK_ROI_DESTINATION;
0202:                }
0203:                if (pb.getIntParameter(9) != 0) {
0204:                    opMask |= MASK_ROTATION;
0205:                }
0206:                if (pb.getObjectParameter(10) != null) {
0207:                    opMask |= MASK_MIRROR_AXIS;
0208:                }
0209:                if (pb.getObjectParameter(11) != null) {
0210:                    opMask |= MASK_ICC_PROFILE;
0211:                }
0212:                if (pb.getObjectParameter(12) != null) {
0213:                    opMask |= MASK_JPEG_QUALITY;
0214:                }
0215:                if (pb.getObjectParameter(13) != null) {
0216:                    opMask |= MASK_JPEG_TABLE;
0217:                }
0218:
0219:                return opMask;
0220:            }
0221:
0222:            /**
0223:             * Returns the server capability mask.
0224:             */
0225:            private static final int getServerCapabilityMask(String URLSpec,
0226:                    RenderedImage lowRes) {
0227:                int vendorID = 255; // Unregistered vendor.
0228:                int serverMask = 0;
0229:
0230:                // Get the server bitmask from the properties of the thumbnail image.
0231:                if (lowRes.getProperty("iip-server") != null
0232:                        && lowRes.getProperty("iip-server") != Image.UndefinedProperty) {
0233:                    String serverString = (String) lowRes
0234:                            .getProperty("iip-server");
0235:                    int dot = serverString.indexOf(".");
0236:                    vendorID = Integer.valueOf(serverString.substring(0, dot))
0237:                            .intValue();
0238:                    serverMask = Integer.valueOf(
0239:                            serverString.substring(dot + 1)).intValue();
0240:                }
0241:
0242:                // If the vendor is not one the three that defined the IIP
0243:                // specification then assume that the response to the OBJ=IIP-server
0244:                // command is inaccurate. This may not be true in general but it
0245:                // is true of the only other IIP server tested with this code.
0246:                if (serverMask != 127 && vendorID != VENDOR_HP
0247:                        && vendorID != VENDOR_LIVE_PICTURE
0248:                        && vendorID != VENDOR_KODAK) {
0249:                    int[] maxSize = (int[]) lowRes.getProperty("max-size");
0250:                    String rgn = "&RGN=0.0,0.0," + (64.0F / maxSize[0]) + ","
0251:                            + (64.0F / maxSize[1]);
0252:
0253:                    // Actually test these capabilities
0254:                    if (canDecode(URLSpec, "&CNT=0.9&WID=64&CVT=JPEG", "JPEG")) {
0255:                        // CVT-JPEG && CVT-MJPEG && CVT-M2JPEG
0256:                        serverMask = SERVER_JPEG_FULL;
0257:                    } else if (canDecode(URLSpec, "&CNT=0.9&WID=64&CVT=FPX",
0258:                            "FPX")) {
0259:                        // CVT-FPX && CVT-MFPX && CVT-M2FPX
0260:                        serverMask = SERVER_FPX_FULL;
0261:                    } else if (canDecode(URLSpec, rgn + "&CVT=JPEG", "JPEG")) {
0262:                        // CVT-JPEG && CVT-MJPEG
0263:                        serverMask = SERVER_JPEG_PARTIAL;
0264:                    } else if (canDecode(URLSpec, rgn + "&CVT=FPX", "FPX")) {
0265:                        // CVT-FPX && CVT-MFPX
0266:                        serverMask = SERVER_FPX_PARTIAL;
0267:                    }
0268:                }
0269:
0270:                return serverMask;
0271:            }
0272:
0273:            /**
0274:             * Test whether an image can be decoded from an IIP CVT URL.
0275:             *
0276:             * @param base The base IIP URL including the image specification.
0277:             * @param suffix The IIP URL suffix including the CVT string.
0278:             * @param fmt The desired format: "JPEG" or "FPX".
0279:             * @return Whether the returned stream can be dedoced successfully.
0280:             */
0281:            private static boolean canDecode(String base, String suffix,
0282:                    String fmt) {
0283:                StringBuffer buf = new StringBuffer(base);
0284:
0285:                URL url = null;
0286:                InputStream stream = null;
0287:                RenderedImage rendering = null;
0288:
0289:                boolean itWorks = false;
0290:
0291:                try {
0292:                    buf.append(suffix);
0293:                    url = new URL(buf.toString());
0294:                    stream = url.openStream();
0295:                    ImageDecoder decoder = ImageCodec.createImageDecoder(fmt,
0296:                            stream, null);
0297:                    rendering = decoder.decodeAsRenderedImage();
0298:                    itWorks = true;
0299:                } catch (Exception e) {
0300:                    itWorks = false; // redundant
0301:                }
0302:
0303:                return itWorks;
0304:            }
0305:
0306:            /**
0307:             * Multiply two matrix parameters and return the result. The number of
0308:             * columns of the first parameter must equal the number of rows of
0309:             * the second parameter. The result will have the same number of rows
0310:             * as the first parameter and the same number of columns as the
0311:             * second parameter.
0312:             */
0313:            private static final double[][] matrixMultiply(double[][] A,
0314:                    double[][] B) {
0315:                if (A[0].length != B.length) {
0316:                    throw new RuntimeException(JaiI18N.getString("IIPCRIF0"));
0317:                }
0318:
0319:                int nRows = A.length;
0320:                int nCols = B[0].length;
0321:                double[][] C = new double[nRows][nCols];
0322:
0323:                int nSum = A[0].length;
0324:                for (int r = 0; r < nRows; r++) {
0325:                    for (int c = 0; c < nCols; c++) {
0326:                        C[r][c] = 0.0;
0327:                        for (int k = 0; k < nSum; k++) {
0328:                            C[r][c] += A[r][k] * B[k][c];
0329:                        }
0330:                    }
0331:                }
0332:
0333:                return C;
0334:            }
0335:
0336:            /**
0337:             * Compose a matrix A and a vector b into an array suitable for the
0338:             * "Bandcombine" operation. The number of rows in the matrix must
0339:             * equal the number of elements in the vector.
0340:             */
0341:            private static final double[][] composeMatrices(double[][] A,
0342:                    double[][] b) {
0343:                int nRows = A.length;
0344:                if (nRows != b.length) {
0345:                    throw new RuntimeException(JaiI18N.getString("IIPCRIF1"));
0346:                } else if (b[0].length != 1) {
0347:                    throw new RuntimeException(JaiI18N.getString("IIPCRIF2"));
0348:                }
0349:                int nCols = A[0].length;
0350:
0351:                double[][] bcMatrix = new double[nRows][nCols + 1];
0352:
0353:                for (int r = 0; r < nRows; r++) {
0354:                    for (int c = 0; c < nCols; c++) {
0355:                        bcMatrix[r][c] = A[r][c];
0356:                    }
0357:                    bcMatrix[r][nCols] = b[r][0];
0358:                }
0359:
0360:                return bcMatrix;
0361:            }
0362:
0363:            /**
0364:             * Generate a matrix which can perform the composite mapping from the
0365:             * original color space to normalized Photo YCC, apply the color-twist
0366:             * transformation, and return normalized Photo YCC to the original
0367:             * color space including casting down opacity and chroma channels where
0368:             * appropriate.
0369:             */
0370:            private static final double[][] getColorTwistMatrix(
0371:                    ColorModel colorModel, ParameterBlock pb) {
0372:                // Convert color-twist matrix to 2D form.
0373:                float[] ctwParam = (float[]) pb.getObjectParameter(3);
0374:                double[][] ctw = new double[4][4];
0375:                int k = 0;
0376:                for (int r = 0; r < 4; r++) {
0377:                    for (int c = 0; c < 4; c++) {
0378:                        ctw[r][c] = ctwParam[k++];
0379:                    }
0380:                }
0381:
0382:                // Calculate composed metric color conversion/color-twist matrix H
0383:                // and constant d.
0384:                double[][] H = null;
0385:                double[][] d = null;
0386:                int csType = colorModel.getColorSpace().getType();
0387:                if (csType == ColorSpace.TYPE_GRAY
0388:                        || csType == ColorSpace.TYPE_RGB) {
0389:                    // Calculate RGBA->YCCA->CTW->RGBA composed matix.
0390:                    H = matrixMultiply(matrixMultiply(YCCA_TO_RGBA, ctw),
0391:                            RGBA_TO_YCCA);
0392:                    d = YCCA_TO_RGBA_CONST;
0393:                } else { // PYCC
0394:                    H = ctw;
0395:                    d = new double[][] { { 0.0 }, { 0.0 }, { 0.0 }, { 0.0 } };
0396:                }
0397:
0398:                // Calculate matrix A and vector b to cast data upwards to 4 bands.
0399:                double[][] A = null;
0400:                double[][] b = null;
0401:                if (csType == ColorSpace.TYPE_GRAY) {
0402:                    if (colorModel.hasAlpha()) {
0403:                        A = new double[][] { { 1.0, 0.0 }, { 1.0, 0.0 },
0404:                                { 1.0, 0.0 }, { 0.0, 1.0 } };
0405:                        b = new double[][] { { 0.0 }, { 0.0 }, { 0.0 }, { 0.0 } };
0406:                    } else {
0407:                        A = new double[][] { { 1.0 }, { 1.0 }, { 1.0 }, { 0.0 } };
0408:                        b = new double[][] { { 0.0 }, { 0.0 }, { 0.0 },
0409:                                { 255.0 } };
0410:                    }
0411:                } else if (!colorModel.hasAlpha()) { // RGB or YCC (no alpha)
0412:                    A = new double[][] { { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 },
0413:                            { 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } };
0414:                    b = new double[][] { { 0.0 }, { 0.0 }, { 0.0 }, { 255.0 } };
0415:                } else { // RGBA or YCCA
0416:                    A = new double[][] { { 1.0, 0.0, 0.0, 0.0 },
0417:                            { 0.0, 1.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0, 0.0 },
0418:                            { 0.0, 0.0, 0.0, 1.0 } };
0419:                    b = new double[][] { { 0.0 }, { 0.0 }, { 0.0 }, { 0.0 } };
0420:                }
0421:
0422:                // Determine whether chroma or opacity may be deleted.
0423:                boolean truncateChroma = false;
0424:                if (csType == ColorSpace.TYPE_GRAY && ctwParam[4] == 0.0F
0425:                        && ctwParam[7] == 0.0F && ctwParam[8] == 0.0F
0426:                        && ctwParam[11] == 0.0F) {
0427:                    truncateChroma = true;
0428:                }
0429:                boolean truncateAlpha = false;
0430:                if (!colorModel.hasAlpha() && ctwParam[15] == 1.0F) {
0431:                    truncateAlpha = true;
0432:                }
0433:
0434:                // Calculate matrix T to truncate data down to alpha-less or
0435:                // chroma-less data as appropriate.
0436:                double[][] T = null;
0437:                if (truncateAlpha && truncateChroma) {
0438:                    T = new double[][] { { 1.0, 0.0, 0.0, 0.0 } };
0439:                } else if (truncateChroma) {
0440:                    T = new double[][] { { 1.0, 0.0, 0.0, 0.0 },
0441:                            { 0.0, 0.0, 0.0, 1.0 } };
0442:                } else if (truncateAlpha) {
0443:                    T = new double[][] { { 1.0, 0.0, 0.0, 0.0 },
0444:                            { 0.0, 1.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0, 0.0 } };
0445:                } else { // Retain all bands
0446:                    T = new double[][] { { 1.0, 0.0, 0.0, 0.0 },
0447:                            { 0.0, 1.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0, 0.0 },
0448:                            { 0.0, 0.0, 0.0, 1.0 } };
0449:                }
0450:
0451:                // Combine the matrices and vectors to get the overall transform.
0452:                double[][] TH = matrixMultiply(T, H);
0453:                double[][] THA = matrixMultiply(TH, A);
0454:                double[][] THb = matrixMultiply(TH, b);
0455:                double[][] THd = matrixMultiply(TH, d);
0456:                double[][] Td = matrixMultiply(T, d);
0457:
0458:                for (int r = 0; r < THb.length; r++) {
0459:                    for (int c = 0; c < THb[r].length; c++) {
0460:                        THb[r][c] += Td[r][c] - THd[r][c];
0461:                    }
0462:                }
0463:
0464:                // Compose the results into a form appropriate for "BandCombine".
0465:                return composeMatrices(THA, THb);
0466:            }
0467:
0468:            /**
0469:             * Creates a lookup table for the contrast operation. This is to be
0470:             * applied to grayscale or RGB data possibly with an alpha channel.
0471:             */
0472:            private static final LookupTableJAI createContrastLUT(float K,
0473:                    int numBands) {
0474:                byte[] contrastTable = new byte[256];
0475:
0476:                double p = 0.43F;
0477:
0478:                // Generate the LUT to be applied to the color band(s).
0479:                for (int i = 0; i < 256; i++) {
0480:                    float j = (float) (i - 127.5F) / 255.0F;
0481:                    float f = 0.0F;
0482:                    if (j < 0.0F) {
0483:                        f = (float) (-p * Math.pow(-j / p, K));
0484:                    } else if (j > 0.0F) {
0485:                        f = (float) (p * Math.pow(j / p, K));
0486:                    }
0487:                    int val = (int) (f * 255.0F + 127.5F);
0488:                    if (val < 0) {
0489:                        contrastTable[i] = 0;
0490:                    } else if (val > 255) {
0491:                        contrastTable[i] = (byte) 255;
0492:                    } else {
0493:                        contrastTable[i] = (byte) (val & 0x000000ff);
0494:                    }
0495:                }
0496:
0497:                // Allocate LUT memory.
0498:                byte[][] data = new byte[numBands][];
0499:
0500:                // Set all LUT color bands to the same previously calculated table.
0501:                // If alpha is present, set the LUT for it to a ramp.
0502:                if (numBands % 2 == 1) { // no alpha channel present
0503:                    for (int i = 0; i < numBands; i++) {
0504:                        data[i] = contrastTable;
0505:                    }
0506:                } else { // alpha channel present
0507:                    for (int i = 0; i < numBands - 1; i++) {
0508:                        data[i] = contrastTable;
0509:                    }
0510:                    data[numBands - 1] = new byte[256];
0511:                    byte[] b = data[numBands - 1];
0512:                    for (int i = 0; i < 256; i++) {
0513:                        b[i] = (byte) i;
0514:                    }
0515:                }
0516:
0517:                return new LookupTableJAI(data);
0518:            }
0519:
0520:            /** Constructor. */
0521:            public IIPCRIF() {
0522:                super ("IIP");
0523:            }
0524:
0525:            /**
0526:             * Performs all operations on the server.
0527:             */
0528:            private RenderedImage serverProc(int serverMask,
0529:                    RenderContext renderContext, ParameterBlock paramBlock,
0530:                    int opMask, RenderedImage lowRes) {
0531:                // Ensure that one of the four expected combinations obtains.
0532:                if ((serverMask & SERVER_JPEG_FULL) != SERVER_JPEG_FULL
0533:                        && (serverMask & SERVER_FPX_FULL) != SERVER_FPX_FULL
0534:                        && (serverMask & SERVER_JPEG_PARTIAL) != SERVER_JPEG_PARTIAL
0535:                        && (serverMask & SERVER_FPX_PARTIAL) != SERVER_FPX_PARTIAL) {
0536:                    return null;
0537:                }
0538:
0539:                ImagingListener listener = ImageUtil
0540:                        .getImagingListener(renderContext);
0541:
0542:                // Set JPEG and full server flags.
0543:                boolean isJPEG = false;
0544:                boolean isFull = false;
0545:                if ((serverMask & SERVER_JPEG_FULL) == SERVER_JPEG_FULL) {
0546:                    isJPEG = isFull = true;
0547:                } else if ((serverMask & SERVER_FPX_FULL) == SERVER_FPX_FULL) {
0548:                    isJPEG = false;
0549:                    isFull = true;
0550:                } else if ((serverMask & SERVER_JPEG_PARTIAL) == SERVER_JPEG_PARTIAL) {
0551:                    isJPEG = true;
0552:                    isFull = false;
0553:                }
0554:
0555:                // Create a StringBuffer for the composed image command URL.
0556:                StringBuffer buf = new StringBuffer((String) paramBlock
0557:                        .getObjectParameter(0));
0558:                //TODO: subImages (how?)
0559:
0560:                // Filtering.
0561:                if ((opMask & MASK_FILTER) != 0) {
0562:                    buf.append("&FTR=" + paramBlock.getFloatParameter(2));
0563:                }
0564:
0565:                // Color-twist.
0566:                if ((opMask & MASK_COLOR_TWIST) != 0) {
0567:                    buf.append("&CTW=");
0568:                    float[] ctw = (float[]) paramBlock.getObjectParameter(3);
0569:                    for (int i = 0; i < ctw.length; i++) {
0570:                        buf.append(ctw[i]);
0571:                        if (i != ctw.length - 1) {
0572:                            buf.append(",");
0573:                        }
0574:                    }
0575:                }
0576:
0577:                // Contrast.
0578:                if ((opMask & MASK_CONTRAST) != 0) {
0579:                    buf.append("&CNT=" + paramBlock.getFloatParameter(4));
0580:                }
0581:
0582:                // Source rectangle of interest.
0583:                if ((opMask & MASK_ROI_SOURCE) != 0) {
0584:                    Rectangle2D roi = (Rectangle2D) paramBlock
0585:                            .getObjectParameter(5);
0586:                    buf.append("&ROI=" + roi.getX() + "," + roi.getY() + ","
0587:                            + roi.getWidth() + "," + roi.getHeight());
0588:                }
0589:
0590:                // If full support for the CVT command is available, decompose the
0591:                // AffineTransform specifying the transformation from renderable to
0592:                // rendered coordinates into a translation, a pure scale, and the
0593:                // residual transformation. The residual transformation may then be
0594:                // concatenated with the server-side affine transform (after
0595:                // inversion), the pure scale may be effected by specifying the WID
0596:                // and HEI composed image command modifiers, and the translation as
0597:                // a subsequent operation. If the WID and HEI modifiers are not
0598:                // available, i.e., the server support is partial, this becomes
0599:                // more problematic. Fortunately no such servers are known to exist.
0600:
0601:                // Initialize the post-processing transform to the identity.
0602:                AffineTransform postTransform = new AffineTransform();
0603:
0604:                // Retrieve (a clone of) the renderable-to-rendered mapping.
0605:                AffineTransform at = (AffineTransform) renderContext
0606:                        .getTransform().clone();
0607:
0608:                // If the translation is non-zero set the post-transform.
0609:                if (at.getTranslateX() != 0.0 || at.getTranslateY() != 0.0) {
0610:                    postTransform.setToTranslation(at.getTranslateX(), at
0611:                            .getTranslateY());
0612:                    double[] m = new double[6];
0613:                    at.getMatrix(m);
0614:                    at.setTransform(m[0], m[1], m[2], m[3], 0.0, 0.0);
0615:                }
0616:
0617:                // Determine the renderable destination region of interest.
0618:                Rectangle2D rgn = null;
0619:                if ((opMask & MASK_ROI_DESTINATION) != 0) {
0620:                    rgn = (Rectangle2D) paramBlock.getObjectParameter(8);
0621:                } else {
0622:                    float aspectRatio = 1.0F;
0623:                    if ((opMask & MASK_ASPECT_RATIO) != 0) {
0624:                        aspectRatio = paramBlock.getFloatParameter(7);
0625:                    } else {
0626:                        aspectRatio = ((Float) (lowRes
0627:                                .getProperty("aspect-ratio"))).floatValue();
0628:                    }
0629:                    rgn = new Rectangle2D.Float(0.0F, 0.0F, aspectRatio, 1.0F);
0630:                }
0631:
0632:                // Apply the renderable-to-rendered mapping to the renderable
0633:                // destination region of interest.
0634:                Rectangle dstROI = at.createTransformedShape(rgn).getBounds();
0635:
0636:                // Calculate the pure scale portion of the
0637:                // renderable-to-rendered mapping.
0638:                AffineTransform scale = AffineTransform.getScaleInstance(dstROI
0639:                        .getWidth()
0640:                        / rgn.getWidth(), dstROI.getHeight() / rgn.getHeight());
0641:
0642:                // Determine the residual mapping.
0643:                try {
0644:                    at.preConcatenate(scale.createInverse());
0645:                } catch (Exception e) {
0646:                    String message = JaiI18N.getString("IIPCRIF6");
0647:                    listener.errorOccurred(message, new ImagingException(
0648:                            message, e), this , false);
0649:                    //            throw new RuntimeException(JaiI18N.getString("IIPCRIF6"));
0650:                }
0651:
0652:                // Compose the inverse residual mapping with the renderable
0653:                // transform.
0654:                AffineTransform afn = (AffineTransform) paramBlock
0655:                        .getObjectParameter(6);
0656:                try {
0657:                    afn.preConcatenate(at.createInverse());
0658:                } catch (Exception e) {
0659:                    String message = JaiI18N.getString("IIPCRIF6");
0660:                    listener.errorOccurred(message, new ImagingException(
0661:                            message, e), this , false);
0662:                    //            throw new RuntimeException(JaiI18N.getString("IIPCRIF6"));
0663:                }
0664:
0665:                if (isFull) {
0666:                    // Append the WID and HEI composed image command modifiers using
0667:                    // the dimensions of the rendered destination region of interest.
0668:                    buf
0669:                            .append("&WID=" + dstROI.width + "&HEI="
0670:                                    + dstROI.height);
0671:                    /* XXX Begin suppressed section.
0672:                    } else if((opMask & MASK_TRANSFORM) != 0) {
0673:                    Point2D[] dstPts =
0674:                        new Point2D[] {new Point2D.Double(rgn.getMinX(),
0675:                                                          rgn.getMinY()),
0676:                                           new Point2D.Double(rgn.getMaxX(),
0677:                                                              rgn.getMinY()),
0678:                                           new Point2D.Double(rgn.getMinX(),
0679:                                                              rgn.getMaxY())};
0680:                    Point2D[] srcPts = new Point2D[3];
0681:                    afn.transform(dstPts, 0, srcPts, 0, 3);
0682:
0683:                    double LLeft = srcPts[0].distance(srcPts[2]);
0684:                    double LTop = srcPts[0].distance(srcPts[1]);
0685:
0686:                    int[] maxSize = (int[])lowRes.getProperty("max-size");
0687:
0688:                    double H = maxSize[1]*LLeft;
0689:                    double W = maxSize[1]*LTop;
0690:
0691:                    double m = Math.max(H, W*(double)maxSize[1]/(double)maxSize[0]);
0692:
0693:                    int Hp = (int)(m + 0.5);
0694:                    int Wp = (int)(m*(double)maxSize[0]/(double)maxSize[1] + 0.5);
0695:                    System.out.println("Estimated dimensions = "+Wp+" x "+Hp);
0696:
0697:                    AffineTransform scl =
0698:                        AffineTransform.getScaleInstance(dstROI.getWidth()/Wp,
0699:                                                         dstROI.getHeight()/Hp);
0700:                    System.out.println("scl = "+scl);
0701:                    afn.preConcatenate(scl);
0702:                    End suppressed section. XXX */
0703:                }
0704:
0705:                // Append the affine tranform composed image command.
0706:                double[] matrix = new double[6];
0707:                afn.getMatrix(matrix);
0708:                buf.append("&AFN=" + matrix[0] + "," + matrix[2] + ",0,"
0709:                        + matrix[4] + "," + matrix[1] + "," + matrix[3] + ",0,"
0710:                        + matrix[5] + ",0,0,1,0,0,0,0,1");
0711:
0712:                // Destination aspect ratio.
0713:                if ((opMask & MASK_ASPECT_RATIO) != 0) {
0714:                    buf.append("&RAR=" + paramBlock.getFloatParameter(7));
0715:                }
0716:
0717:                // Destination rectangle of interest.
0718:                if ((opMask & MASK_ROI_DESTINATION) != 0) {
0719:                    Rectangle2D dstRGN = (Rectangle2D) paramBlock
0720:                            .getObjectParameter(8);
0721:                    buf.append("&RGN=" + dstRGN.getX() + "," + dstRGN.getY()
0722:                            + "," + dstRGN.getWidth() + ","
0723:                            + dstRGN.getHeight());
0724:                }
0725:
0726:                // Rotation and mirroring.
0727:                if (isFull) {
0728:                    if ((opMask & MASK_ROTATION) != 0
0729:                            || (opMask & MASK_MIRROR_AXIS) != 0) {
0730:                        buf.append("&RFM=" + paramBlock.getIntParameter(9));
0731:                        if ((opMask & MASK_MIRROR_AXIS) != 0) {
0732:                            String axis = (String) paramBlock
0733:                                    .getObjectParameter(10);
0734:                            if (axis.equalsIgnoreCase("x")) {
0735:                                buf.append(",0");
0736:                            } else {
0737:                                buf.append(",90");
0738:                            }
0739:                        }
0740:                    }
0741:                }
0742:
0743:                // ICC profile.
0744:                if ((opMask & MASK_ICC_PROFILE) != 0) {
0745:                    // According to the IIP specification this is not supported
0746:                    // over HTTP connections and that is all that is available from
0747:                    // the vendors right now, i.e., no socket connections are
0748:                    // available (includes LivePicture and TrueSpectra).
0749:                }
0750:
0751:                // JPEG quality and compression group index.
0752:                if (isJPEG) {
0753:                    if ((opMask & MASK_JPEG_QUALITY) != 0) {
0754:                        buf.append("&QLT=" + paramBlock.getIntParameter(12));
0755:                    }
0756:
0757:                    if ((opMask & MASK_JPEG_TABLE) != 0) {
0758:                        buf.append("&CIN=" + paramBlock.getIntParameter(13));
0759:                    }
0760:                }
0761:
0762:                // Set the format string.
0763:                String format = isJPEG ? "JPEG" : "FPX";
0764:
0765:                // Append the CVT command.
0766:                buf.append("&CVT=" + format);
0767:
0768:                // Create a URL with the CVT string, open a stream from it, and
0769:                // decode the image using the appropriate decoder.
0770:                InputStream stream = null;
0771:                RenderedImage rendering = null;
0772:                try {
0773:                    URL url = new URL(buf.toString());
0774:                    stream = url.openStream();
0775:                    MemoryCacheSeekableStream sStream = new MemoryCacheSeekableStream(
0776:                            stream);
0777:                    rendering = JAI.create(format, sStream);
0778:                } catch (Exception e) {
0779:                    String message = JaiI18N.getString("IIPCRIF7") + " "
0780:                            + buf.toString();
0781:                    listener.errorOccurred(message, new ImagingException(
0782:                            message, e), this , false);
0783:                    //            throw new RuntimeException(e.getClass()+" "+e.getMessage());
0784:                }
0785:
0786:                // If WID and HEI modifiers are unavailable add scale.
0787:                if (!isFull) {
0788:                    postTransform.scale(dstROI.getWidth()
0789:                            / rendering.getWidth(), dstROI.getHeight()
0790:                            / rendering.getHeight());
0791:                }
0792:
0793:                // Translate (and scale) the result if necessary.
0794:                if (!postTransform.isIdentity()) {
0795:                    Interpolation interp = Interpolation
0796:                            .getInstance(Interpolation.INTERP_NEAREST);
0797:                    RenderingHints hints = renderContext.getRenderingHints();
0798:                    if (hints != null
0799:                            && hints.containsKey(JAI.KEY_INTERPOLATION)) {
0800:                        interp = (Interpolation) hints
0801:                                .get(JAI.KEY_INTERPOLATION);
0802:                    }
0803:                    rendering = JAI.create("affine", rendering, postTransform,
0804:                            interp);
0805:                }
0806:
0807:                return rendering;
0808:            }
0809:
0810:            /**
0811:             * Performs all operations on the client.
0812:             */
0813:            private RenderedImage clientProc(RenderContext renderContext,
0814:                    ParameterBlock paramBlock, int opMask, RenderedImage lowRes) {
0815:                // Cache RenderContext components.
0816:                AffineTransform at = renderContext.getTransform();
0817:                RenderingHints hints = renderContext.getRenderingHints();
0818:
0819:                ImagingListener listener = ImageUtil
0820:                        .getImagingListener(renderContext);
0821:
0822:                // Obtain the number of levels and the size of the largest one.
0823:                int[] maxSize = (int[]) lowRes.getProperty("max-size");
0824:                int maxWidth = maxSize[0];
0825:                int maxHeight = maxSize[1];
0826:                int numLevels = ((Integer) lowRes
0827:                        .getProperty("resolution-number")).intValue();
0828:
0829:                // Calculate the aspect ratios.
0830:                float aspectRatioSource = (float) maxWidth / (float) maxHeight;
0831:                float aspectRatio = (opMask & MASK_ASPECT_RATIO) != 0 ? paramBlock
0832:                        .getFloatParameter(7)
0833:                        : aspectRatioSource;
0834:
0835:                // Determine the bounds of the destination image.
0836:                Rectangle2D bounds2D = new Rectangle2D.Float(0.0F, 0.0F,
0837:                        aspectRatio, 1.0F);
0838:
0839:                // Determine the dimensions of the rendered destination image.
0840:                int width;
0841:                int height;
0842:                if (at.isIdentity()) { // Default rendering.
0843:                    AffineTransform afn = (AffineTransform) paramBlock
0844:                            .getObjectParameter(6);
0845:                    Rectangle2D bounds = afn.createTransformedShape(bounds2D)
0846:                            .getBounds2D();
0847:                    double H = maxHeight * bounds.getHeight();
0848:                    double W = maxHeight * bounds.getWidth();
0849:                    double m = Math.max(H, W / aspectRatioSource);
0850:                    height = (int) (m + 0.5);
0851:                    width = (int) (aspectRatioSource * m + 0.5);
0852:                    at = AffineTransform.getScaleInstance(width, height);
0853:                    renderContext = (RenderContext) renderContext.clone();
0854:                    renderContext.setTransform(at);
0855:                } else {
0856:                    Rectangle bounds = at.createTransformedShape(bounds2D)
0857:                            .getBounds();
0858:                    width = bounds.width;
0859:                    height = bounds.height;
0860:                }
0861:
0862:                // Determine which resolution level of the IIP image to request.
0863:                int res = numLevels - 1;
0864:                int hRes = maxHeight;
0865:                while (res > 0) {
0866:                    hRes = (int) ((hRes + 1.0F) / 2.0F); // get the next height
0867:                    if (hRes < height) { // stop if the next height is too small
0868:                        break;
0869:                    }
0870:                    res--;
0871:                }
0872:
0873:                // Create a RenderableImage from the selected resolution level.
0874:                int[] subImageArray = (int[]) paramBlock.getObjectParameter(1);
0875:                int subImage = subImageArray.length < res + 1 ? 0
0876:                        : subImageArray[res];
0877:                if (subImage < 0) {
0878:                    subImage = 0;
0879:                }
0880:                ParameterBlock pb = new ParameterBlock();
0881:                pb.add(paramBlock.getObjectParameter(0)).add(res).add(subImage);
0882:                RenderedImage iipRes = JAI.create("iipresolution", pb);
0883:                Vector sources = new Vector(1);
0884:                sources.add(iipRes);
0885:                RenderableImage ri = new MultiResolutionRenderableImage(
0886:                        sources, 0.0F, 0.0F, 1.0F);
0887:
0888:                // Filtering.
0889:                if ((opMask & MASK_FILTER) != 0) {
0890:                    float filter = paramBlock.getFloatParameter(2);
0891:                    pb = (new ParameterBlock()).addSource(ri).add(filter);
0892:                    ri = new RenderableImageOp(new FilterCRIF(), pb);
0893:                }
0894:
0895:                // Color-twist.
0896:                // Cache the original number of bands in case the number of bands
0897:                // changes due to addition of chroma and/or alpha channels in the
0898:                // color-twist procedure.
0899:                int nBands = iipRes.getSampleModel().getNumBands();
0900:                if ((opMask & MASK_COLOR_TWIST) != 0) {
0901:                    double[][] ctw = getColorTwistMatrix(
0902:                            iipRes.getColorModel(), paramBlock);
0903:                    pb = (new ParameterBlock()).addSource(ri).add(ctw);
0904:                    ri = JAI.createRenderable("bandcombine", pb);
0905:                    nBands = ctw.length;
0906:                }
0907:
0908:                // Contrast.
0909:                if ((opMask & MASK_CONTRAST) != 0) {
0910:                    int csType = iipRes.getColorModel().getColorSpace()
0911:                            .getType();
0912:                    boolean isPYCC = csType != ColorSpace.TYPE_GRAY
0913:                            && csType != ColorSpace.TYPE_RGB;
0914:
0915:                    if (isPYCC) {
0916:                        double[][] matrix;
0917:                        if (nBands == 3) { // PYCC
0918:                            matrix = composeMatrices(YCC_TO_RGB,
0919:                                    YCC_TO_RGB_CONST);
0920:                        } else { // PYCC-A
0921:                            matrix = composeMatrices(YCCA_TO_RGBA,
0922:                                    YCCA_TO_RGBA_CONST);
0923:                        }
0924:                        pb = (new ParameterBlock()).addSource(ri).add(matrix);
0925:                        ri = JAI.createRenderable("bandcombine", pb);
0926:                    }
0927:
0928:                    float contrast = paramBlock.getFloatParameter(4);
0929:                    LookupTableJAI lut = createContrastLUT(contrast, nBands);
0930:
0931:                    pb = (new ParameterBlock()).addSource(ri).add(lut);
0932:                    ri = JAI.createRenderable("lookup", pb);
0933:
0934:                    if (isPYCC) {
0935:                        double[][] matrix;
0936:                        if (nBands == 3) { // PYCC
0937:                            matrix = composeMatrices(RGB_TO_YCC,
0938:                                    RGB_TO_YCC_CONST);
0939:                        } else { // PYCC-A
0940:                            matrix = composeMatrices(RGBA_TO_YCCA,
0941:                                    RGBA_TO_YCCA_CONST);
0942:                        }
0943:                        pb = (new ParameterBlock()).addSource(ri).add(matrix);
0944:                        ri = JAI.createRenderable("bandcombine", pb);
0945:                    }
0946:                }
0947:
0948:                // Source rectangle of interest.
0949:                if ((opMask & MASK_ROI_SOURCE) != 0) {
0950:                    // Get the source rectangle of interest.
0951:                    Rectangle2D rect = (Rectangle2D) paramBlock
0952:                            .getObjectParameter(5);
0953:
0954:                    // Check for intersection with source bounds.
0955:                    if (!rect.intersects(0.0, 0.0, aspectRatioSource, 1.0)) {
0956:                        throw new RuntimeException(JaiI18N
0957:                                .getString("IIPCRIF5"));
0958:                    }
0959:
0960:                    // Create the source rectangle.
0961:                    Rectangle2D rectS = new Rectangle2D.Float(0.0F, 0.0F,
0962:                            aspectRatioSource, 1.0F);
0963:
0964:                    // Crop out the desired region.
0965:                    if (!rect.equals(rectS)) {
0966:                        // Clip to the source bounds.
0967:                        rect = rect.createIntersection(rectS);
0968:
0969:                        // Crop to the clipped rectangle of interest.
0970:                        pb = (new ParameterBlock()).addSource(ri);
0971:                        pb.add((float) rect.getMinX()).add(
0972:                                (float) rect.getMinY());
0973:                        pb.add((float) rect.getWidth()).add(
0974:                                (float) rect.getHeight());
0975:                        ri = JAI.createRenderable("crop", pb);
0976:
0977:                        /* XXX
0978:                        // Embed the cropped image in an image the size of the source.
0979:                        pb = (new ParameterBlock()).addSource(ri);
0980:                        pb.add((float)rectS.getMinX()).add((float)rectS.getMinY());
0981:                        pb.add((float)rectS.getWidth()).add((float)rectS.getHeight());
0982:                        ri = JAI.createRenderable("crop", pb);
0983:                         */
0984:                    }
0985:                }
0986:
0987:                // Spatial orientation.
0988:                if ((opMask & MASK_TRANSFORM) != 0) {
0989:                    AffineTransform afn = (AffineTransform) paramBlock
0990:                            .getObjectParameter(6);
0991:                    try {
0992:                        // The transform parameter is a backward mapping so invert it.
0993:                        afn = afn.createInverse();
0994:                    } catch (java.awt.geom.NoninvertibleTransformException e) {
0995:                        // This should never happen due to descriptor check.
0996:                        listener.errorOccurred(JaiI18N
0997:                                .getString("AffineNotInvertible"), e, this ,
0998:                                false);
0999:
1000:                    }
1001:                    pb = (new ParameterBlock()).addSource(ri).add(afn);
1002:                    if (hints != null
1003:                            && hints.containsKey(JAI.KEY_INTERPOLATION)) {
1004:                        pb.add(hints.get(JAI.KEY_INTERPOLATION));
1005:                    }
1006:                    ri = JAI.createRenderable("affine", pb);
1007:                }
1008:
1009:                // Destination rectangle of interest.
1010:                // Set the destination rectangle of interest.
1011:                Rectangle2D rgn = (opMask & MASK_ROI_DESTINATION) != 0 ? (Rectangle2D) paramBlock
1012:                        .getObjectParameter(8)
1013:                        : bounds2D;
1014:
1015:                // Verify that the region is non-empty.
1016:                if (rgn.isEmpty()) {
1017:                    throw new RuntimeException(JaiI18N.getString("IIPCRIF3"));
1018:                }
1019:
1020:                // Create a Rectangle2D for the current image.
1021:                Rectangle2D riRect = new Rectangle2D.Float(
1022:                        (float) ri.getMinX(), (float) ri.getMinY(), (float) ri
1023:                                .getWidth(), (float) ri.getHeight());
1024:
1025:                // If the current image bounds are not those of the requested
1026:                // region then crop the image.
1027:                if (!rgn.equals(riRect)) {
1028:                    // Intersect rgn with source image bounds.
1029:                    rgn = rgn.createIntersection(riRect);
1030:
1031:                    // Crop to the rectangle of interest.
1032:                    pb = (new ParameterBlock()).addSource(ri);
1033:                    pb.add((float) rgn.getMinX()).add((float) rgn.getMinY());
1034:                    pb.add((float) rgn.getWidth()).add((float) rgn.getHeight());
1035:                    ri = JAI.createRenderable("crop", pb);
1036:                }
1037:
1038:                // Return the rendering.
1039:                return ri.createRendering(renderContext);
1040:            }
1041:
1042:            /**
1043:             * Returns the default rendering of the RenderableImage produced by
1044:             * the "iip" operation.
1045:             */
1046:            public RenderedImage create(ParameterBlock paramBlock,
1047:                    RenderingHints renderHints) {
1048:                RenderableImage iipImage = JAI.createRenderable("iip",
1049:                        paramBlock);
1050:
1051:                return iipImage.createDefaultRendering();
1052:            }
1053:
1054:            /**
1055:             * Applies the specified set of operations to the IIP image
1056:             * and returns a RenderedImage that satisfies the rendering context
1057:             * provided.
1058:             */
1059:            public RenderedImage create(RenderContext renderContext,
1060:                    ParameterBlock paramBlock) {
1061:                // Get the operation mask.
1062:                int opMask = getOperationMask(paramBlock);
1063:
1064:                ImagingListener listener = ImageUtil
1065:                        .getImagingListener(renderContext);
1066:
1067:                // Get the lowest resolution level of the IIP image for property use.
1068:                ParameterBlock pb = new ParameterBlock();
1069:                int[] subImageArray = (int[]) paramBlock.getObjectParameter(1);
1070:                pb.add(paramBlock.getObjectParameter(0)).add(0).add(
1071:                        subImageArray[0]);
1072:                RenderedImage lowRes = JAI.create("iipresolution", pb);
1073:
1074:                // Get the server capability mask.
1075:                int serverMask = getServerCapabilityMask((String) paramBlock
1076:                        .getObjectParameter(0), lowRes);
1077:
1078:                RenderedImage rendering = null;
1079:
1080:                // Select the processing path based on the server's capabilities.
1081:                if ((serverMask & SERVER_JPEG_FULL) == SERVER_JPEG_FULL
1082:                        || (serverMask & SERVER_FPX_FULL) == SERVER_FPX_FULL
1083:                        || (serverMask & SERVER_JPEG_PARTIAL) == SERVER_JPEG_PARTIAL
1084:                        || (serverMask & SERVER_FPX_PARTIAL) == SERVER_FPX_PARTIAL) {
1085:                    // All (FULL) or most (PARTIAL) ops on server
1086:                    rendering = serverProc(serverMask, renderContext,
1087:                            paramBlock, opMask, lowRes);
1088:                } else {
1089:                    // All ops on client
1090:                    rendering = clientProc(renderContext, paramBlock, opMask,
1091:                            lowRes);
1092:
1093:                    // Do special processing if source rectangle of interest given.
1094:                    // The following approach works but is rather slow.
1095:                    if ((opMask & MASK_ROI_SOURCE) != 0) {
1096:                        // Retrieve the source rectangle of interest.
1097:                        Rectangle2D rgn = (Rectangle2D) paramBlock
1098:                                .getObjectParameter(5);
1099:
1100:                        // Retrieve a clone of the renderable transform.
1101:                        AffineTransform at = (AffineTransform) ((AffineTransform) (paramBlock
1102:                                .getObjectParameter(6))).clone();
1103:
1104:                        // If the transform is not the identity, invert it.
1105:                        if (!at.isIdentity()) {
1106:                            try {
1107:                                at = at.createInverse();
1108:                            } catch (Exception e) {
1109:                                String message = JaiI18N.getString("IIPCRIF6");
1110:                                listener.errorOccurred(message,
1111:                                        new ImagingException(message, e), this ,
1112:                                        false);
1113:
1114:                                //                        throw new RuntimeException(JaiI18N.getString("IIPCRIF6"));
1115:                            }
1116:                        }
1117:
1118:                        // Compose the inverted renderable transform with the
1119:                        // renderable-to-rendered transform to get the transform
1120:                        // from source renderable coordinates to destination
1121:                        // rendered coordinates.
1122:                        at.preConcatenate(renderContext.getTransform());
1123:
1124:                        // Create an ROI in destination rendered space.
1125:                        ROIShape roi = new ROIShape(at
1126:                                .createTransformedShape(rgn));
1127:
1128:                        // Create a TiledImage to contain the masked result.
1129:                        TiledImage ti = new TiledImage(rendering.getMinX(),
1130:                                rendering.getMinY(), rendering.getWidth(),
1131:                                rendering.getHeight(), rendering
1132:                                        .getTileGridXOffset(), rendering
1133:                                        .getTileGridYOffset(), rendering
1134:                                        .getSampleModel(), rendering
1135:                                        .getColorModel());
1136:
1137:                        // Set the TiledImage data source to the rendering.
1138:                        ti.set(rendering, roi);
1139:
1140:                        // Create a constant-valued image for the background.
1141:                        pb = new ParameterBlock();
1142:                        pb.add((float) ti.getWidth());
1143:                        pb.add((float) ti.getHeight());
1144:                        Byte[] bandValues = new Byte[ti.getSampleModel()
1145:                                .getNumBands()];
1146:                        for (int b = 0; b < bandValues.length; b++) {
1147:                            bandValues[b] = new Byte((byte) 255);
1148:                        }
1149:                        pb.add(bandValues);
1150:
1151:                        ImageLayout il = new ImageLayout();
1152:                        il.setSampleModel(ti.getSampleModel());
1153:                        RenderingHints rh = new RenderingHints(
1154:                                JAI.KEY_IMAGE_LAYOUT, il);
1155:
1156:                        PlanarImage constImage = JAI.create("constant", pb, rh);
1157:
1158:                        // Compute a complement ROI.
1159:                        ROI complementROI = (new ROIShape(ti.getBounds()))
1160:                                .subtract(roi);
1161:                        ;
1162:
1163:                        // Fill the background.
1164:                        int maxTileY = ti.getMaxTileY();
1165:                        int maxTileX = ti.getMaxTileX();
1166:                        for (int j = ti.getMinTileY(); j <= maxTileY; j++) {
1167:                            for (int i = ti.getMinTileX(); i <= maxTileX; i++) {
1168:                                if (!roi.intersects(ti.getTileRect(i, j))) {
1169:                                    ti.setData(constImage.getTile(i, j),
1170:                                            complementROI);
1171:                                }
1172:                            }
1173:                        }
1174:
1175:                        // Set the rendering to the TiledImage.
1176:                        rendering = ti;
1177:                    }
1178:                }
1179:
1180:                // If the server supports only the first tier of composed image
1181:                // command modifiers or none at all then the "RFM" modifier
1182:                // effect must be replicated on the client if this would be
1183:                // required by the supplied parameters.
1184:                if ((serverMask & SERVER_JPEG_FULL) != SERVER_JPEG_FULL
1185:                        && (serverMask & SERVER_FPX_FULL) != SERVER_FPX_FULL) {
1186:                    if ((opMask & MASK_ROTATION) != 0) {
1187:                        // NOTE: The transpose operation uses clockwise rotation
1188:                        // whereas this operation expects counterclockwise.
1189:                        EnumeratedParameter transposeType = null;
1190:                        switch (paramBlock.getIntParameter(9)) {
1191:                        case 90:
1192:                            transposeType = TransposeDescriptor.ROTATE_270;
1193:                            break;
1194:                        case 180:
1195:                            transposeType = TransposeDescriptor.ROTATE_180;
1196:                            break;
1197:                        case 270:
1198:                            transposeType = TransposeDescriptor.ROTATE_90;
1199:                            break;
1200:                        }
1201:                        if (transposeType != null) { // deliberately redundant test
1202:                            rendering = JAI.create("transpose", rendering,
1203:                                    transposeType);
1204:                        }
1205:                    }
1206:
1207:                    if ((opMask & MASK_MIRROR_AXIS) != 0) {
1208:                        String axis = (String) paramBlock
1209:                                .getObjectParameter(10);
1210:                        EnumeratedParameter transposeType = axis
1211:                                .equalsIgnoreCase("x") ? TransposeDescriptor.FLIP_VERTICAL
1212:                                : TransposeDescriptor.FLIP_HORIZONTAL;
1213:                        rendering = JAI.create("transpose", rendering,
1214:                                transposeType);
1215:                    }
1216:                }
1217:
1218:                return rendering;
1219:            }
1220:
1221:            /**
1222:             * Returns the bounds of the RenderableImage.  This will be the
1223:             * rendering-independent destination rectangle of interest if supplied
1224:             * or the rendering-independent destination image bounds if not.
1225:             */
1226:            public Rectangle2D getBounds2D(ParameterBlock paramBlock) {
1227:                int opMask = getOperationMask(paramBlock);
1228:
1229:                if ((opMask & MASK_ROI_DESTINATION) != 0) {
1230:                    return (Rectangle2D) paramBlock.getObjectParameter(8);
1231:                }
1232:
1233:                float aspectRatioDestination;
1234:                if ((opMask & MASK_ASPECT_RATIO) != 0) {
1235:                    aspectRatioDestination = paramBlock.getFloatParameter(7);
1236:                } else {
1237:                    // Get the lowest resolution level of the IIP image.
1238:                    ParameterBlock pb = new ParameterBlock();
1239:                    int[] subImageArray = (int[]) paramBlock
1240:                            .getObjectParameter(1);
1241:                    pb.add(paramBlock.getObjectParameter(0));
1242:                    pb.add(0).add(subImageArray[0]);
1243:                    RenderedImage lowRes = JAI.create("iipresolution", pb);
1244:
1245:                    int[] maxSize = (int[]) lowRes.getProperty("max-size");
1246:
1247:                    aspectRatioDestination = (float) maxSize[0]
1248:                            / (float) maxSize[1];
1249:                }
1250:
1251:                return new Rectangle2D.Float(0.0F, 0.0F,
1252:                        aspectRatioDestination, 1.0F);
1253:            }
1254:
1255:            public static void main(String[] args) {
1256:                int nr = 0;
1257:                int nc = 0;
1258:
1259:                double[][] x = matrixMultiply(RGBA_TO_YCCA, YCCA_TO_RGBA);
1260:                nr = x.length;
1261:                nc = x[0].length;
1262:                for (int r = 0; r < nr; r++) {
1263:                    for (int c = 0; c < nc; c++) {
1264:                        System.out.print(x[r][c] + " ");
1265:                    }
1266:                    System.out.println("");
1267:                }
1268:                System.out.println("");
1269:
1270:                x = matrixMultiply(RGB_TO_YCC, YCC_TO_RGB);
1271:                nr = x.length;
1272:                nc = x[0].length;
1273:                for (int r = 0; r < nr; r++) {
1274:                    for (int c = 0; c < nc; c++) {
1275:                        System.out.print(x[r][c] + " ");
1276:                    }
1277:                    System.out.println("");
1278:                }
1279:                System.out.println("");
1280:
1281:                double[][] b = new double[][] { { 1.0 }, { 2.0 }, { 3.0 },
1282:                        { 4.0 } };
1283:                double[][] A = composeMatrices(YCCA_TO_RGBA, b);
1284:                nr = A.length;
1285:                nc = A[0].length;
1286:                for (int r = 0; r < nr; r++) {
1287:                    for (int c = 0; c < nc; c++) {
1288:                        System.out.print(A[r][c] + " ");
1289:                    }
1290:                    System.out.println("");
1291:                }
1292:                System.out.println("");
1293:
1294:                double[][] d4 = matrixMultiply(RGBA_TO_YCCA, YCCA_TO_RGBA_CONST);
1295:                nr = d4.length;
1296:                nc = d4[0].length;
1297:                for (int r = 0; r < nr; r++) {
1298:                    for (int c = 0; c < nc; c++) {
1299:                        System.out.print(-d4[r][c] + " ");
1300:                    }
1301:                    System.out.println("");
1302:                }
1303:                System.out.println("");
1304:
1305:                double[][] d3 = matrixMultiply(RGB_TO_YCC, YCC_TO_RGB_CONST);
1306:                nr = d3.length;
1307:                nc = d3[0].length;
1308:                for (int r = 0; r < nr; r++) {
1309:                    for (int c = 0; c < nc; c++) {
1310:                        System.out.print(-d3[r][c] + " ");
1311:                    }
1312:                    System.out.println("");
1313:                }
1314:                System.out.println("");
1315:            }
1316:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.